瀏覽代碼

Add psqueue initial changes and tests

Dalibor Votruba 2 年之前
父節點
當前提交
8255e39f08

+ 48 - 0
qdr.fnd.core.pqueue.test/Mocks/LogMock.cs

@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Quadarax.Foundation.Core.Logging;
+
+namespace qdr.fnd.core.pqueue.test.Mocks
+{
+    public class LogMock : ILog
+    {
+        #region Implementation of ILog
+
+        /// <inheritdoc />
+        public void Log(LogSeverityEnum severity, int code, string message)
+        {
+            Trace.WriteLine($"[{code}]: {message}", severity.ToString());
+        }
+
+        /// <inheritdoc />
+        public void Log(LogSeverityEnum severity, string message)
+        {
+            Trace.WriteLine($"{message}", severity.ToString());
+        }
+
+        /// <inheritdoc />
+        public void Log(LogSeverityEnum severity, string message, Exception? exception)
+        {
+            if (exception != null)
+                Trace.WriteLine($"{message}\n{exception}", severity.ToString());
+            else
+                Trace.WriteLine($"{message}", severity.ToString());
+        }
+
+        /// <inheritdoc />
+        public void Log(LogSeverityEnum severity, int code, string message, Exception? exception)
+        {
+            if (exception != null)
+                Trace.WriteLine($"[{code}] {message}\n{exception}", severity.ToString());
+            else
+                Trace.WriteLine($"[{code}] {message}", severity.ToString());
+
+        }
+
+        #endregion
+    }
+}

+ 28 - 0
qdr.fnd.core.pqueue.test/Mocks/LoggerMock.cs

@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Quadarax.Foundation.Core.Logging;
+
+namespace qdr.fnd.core.pqueue.test.Mocks
+{
+    public class LoggerMock : ILogger
+    {
+        #region Implementation of ILogger
+
+        /// <inheritdoc />
+        public ILog GetLogger(string loggerName)
+        {
+            return new LogMock();
+        }
+
+        /// <inheritdoc />
+        public ILog GetLogger(Type loggerForType)
+        {
+            return new LogMock();
+        }
+
+        #endregion
+    }
+}

+ 47 - 0
qdr.fnd.core.pqueue.test/PsQueueItemTest.cs

@@ -0,0 +1,47 @@
+using qdr.fnd.core.pqueue.Enums;
+using qdr.fnd.core.pqueue.test.Mocks;
+
+namespace qdr.fnd.core.pqueue.test
+{
+    public class PsQueueItemTest
+    {
+        private LoggerMock _logger;
+        private string _id = "id";
+        private string _owner = "owner";
+        private string _ownerRef = "ownerRef";
+        private string _providerClass = "providerClass";
+        private int _initialAttempts = 10;
+        private DateTime _created = DateTime.Now;
+        private PsStatusEnum _status = PsStatusEnum.New;
+
+        [SetUp]
+        public void Setup()
+        {
+            _logger = new LoggerMock();
+        }
+
+        [Test(TestOf=typeof(PsQueueItem))]
+        public void ConstructorOk()
+        {
+            var item = new PsQueueItem(_id, _owner,_ownerRef,_providerClass,_created);
+            Assert.That(item.Id, Is.EqualTo(_id));
+            Assert.That(item.Owner, Is.EqualTo(_owner));
+            Assert.That(item.OwnerRef, Is.EqualTo(_ownerRef));
+            Assert.That(item.ProcessProviderClass, Is.EqualTo(_providerClass));
+            Assert.That(item.Created, Is.EqualTo(_created));
+            Assert.That(item.Status, Is.EqualTo(_status));
+            Assert.That(item.Arguments, Is.Not.Null);
+            Assert.That(item.StatusJournal, Is.Not.Null);
+            Assert.That(item.Log, Is.Not.Null);
+            Assert.That(item.IsStatusTransient, Is.False);
+
+            item = new PsQueueItem(_id);
+            Assert.That(item.Id, Is.EqualTo(_id));
+            Assert.That(item.Arguments, Is.Not.Null);
+            Assert.That(item.StatusJournal, Is.Not.Null);
+            Assert.That(item.Log, Is.Not.Null);
+            Assert.That(item.IsStatusTransient, Is.False);
+            Assert.That(item.Status, Is.EqualTo(_status));
+        }
+    }
+}

+ 30 - 0
qdr.fnd.core.pqueue.test/Storages/BaseStorageProviderTest.cs

@@ -0,0 +1,30 @@
+using qdr.fnd.core.pqueue.Storages;
+using qdr.fnd.core.pqueue.test.Mocks;
+using qdr.fnd.core.pqueue.test.Storages.Mocks;
+
+namespace qdr.fnd.core.pqueue.test.Storages
+{
+    public class BaseStorageProviderTest
+    {
+        private LoggerMock _logger;
+        
+        
+        [Test(TestOf=typeof(BaseStorageProvider))]
+        public void CreateItemOk()
+        {
+            var provider = new BaseStorageProviderMock(_logger);
+            var itemId = provider.CreateItem("own", "own-ref", "prv-class", 10, null);
+            itemId = provider.CreateItem("own", "own-ref", "prv-class", 10, DateTime.Now);
+            Assert.That(itemId, Is.EqualTo(BaseStorageProviderMock.TestId));
+        }
+
+        [Test(TestOf=typeof(BaseStorageProvider))]
+        public void CreateItemFail()
+        {
+            var provider = new BaseStorageProviderMock(_logger);
+            Assert.Throws<ArgumentNullException>(()=>provider.CreateItem(string.Empty, "own-ref", "prv-class", 10, null));
+            Assert.Throws<ArgumentNullException>(()=>provider.CreateItem("own", string.Empty, "prv-class", 10, null));
+            Assert.Throws<ArgumentNullException>(()=>provider.CreateItem("own", "own-ref", string.Empty, 10, null));
+        }
+    }
+}

+ 72 - 0
qdr.fnd.core.pqueue.test/Storages/MemoryStorageProviderTest.cs

@@ -0,0 +1,72 @@
+using qdr.fnd.core.pqueue.Exceptions;
+using qdr.fnd.core.pqueue.Storages;
+using qdr.fnd.core.pqueue.test.Mocks;
+using qdr.fnd.core.pqueue.test.Storages.Mocks;
+
+namespace qdr.fnd.core.pqueue.test.Storages
+{
+    public class MemoryStorageProviderTest
+    {
+        private LoggerMock _logger;
+
+
+        private string _id = "id";
+        private string _owner = "owner";
+        private string _ownerRef = "ownerRef";
+        private string _providerClass = "providerClass";
+        private int _initialAttempts = 10;
+        private DateTime _created = DateTime.Now;
+
+        [SetUp]
+        public void Setup()
+        {
+            _logger = new LoggerMock();
+        }
+
+        [Test(TestOf=typeof(MemoryStorageProvider))]
+        public void ConstructorOk()
+        {
+            var provider = new MemoryStorageProvider(_logger);
+        }
+
+        [Test(TestOf=typeof(MemoryStorageProvider))]
+        public void CreateItemOk()
+        {
+            var provider = new MemoryStorageProvider(_logger);
+            var itemId = provider.CreateItem(_owner, _ownerRef, _providerClass, _initialAttempts, _created);
+            Assert.That(string.IsNullOrEmpty(itemId), Is.False);
+        }
+        [Test(TestOf=typeof(MemoryStorageProvider))]
+        public void ContainsItemOk()
+        {
+            var provider = new MemoryStorageProvider(_logger);
+            var itemId = provider.CreateItem(_owner, _ownerRef, _providerClass, _initialAttempts, _created);
+            Assert.That(provider.ContainsItem(itemId), Is.True);
+            Assert.That(provider.ContainsItem("not-exist"), Is.False);
+        }
+
+        [Test(TestOf=typeof(MemoryStorageProvider))]
+        public void DeleteItemOk()
+        {
+            var provider = new MemoryStorageProvider(_logger);
+            var itemId = provider.CreateItem(_owner, _ownerRef, _providerClass, _initialAttempts, _created);
+            provider.DeleteItem(itemId);
+            Assert.That(provider.ContainsItem(itemId), Is.False);
+        }
+
+        [Test(TestOf = typeof(MemoryStorageProvider))]
+        public void DeleteItemFail()
+        {
+            var provider = new MemoryStorageProvider(_logger);
+            var itemId = provider.CreateItem(_owner, _ownerRef, _providerClass, _initialAttempts, _created);
+            provider.DeleteItem(itemId);
+            Assert.Throws<ArgumentNullException>(() => provider.DeleteItem(string.Empty));
+            var psEx = Assert.Throws<PSStorageException>(() => provider.DeleteItem(itemId));
+            Assert.That(psEx, Is.Not.Null);
+            Assert.That(psEx.Code, Is.EqualTo(PSStorageException.ErrorCodes.PsStorageItemNotFound));
+        }
+
+
+
+    }
+}

+ 52 - 0
qdr.fnd.core.pqueue.test/Storages/Mocks/BaseStorageProviderMock.cs

@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using qdr.fnd.core.pqueue.Storages;
+using Quadarax.Foundation.Core.Logging;
+
+namespace qdr.fnd.core.pqueue.test.Storages.Mocks
+{
+    internal class BaseStorageProviderMock : BaseStorageProvider
+    {
+        public const string TestId = "TestId";
+        public bool ReturnContainsItem { get; set; } = true;
+
+        /// <inheritdoc />
+        public BaseStorageProviderMock(ILogger? logger) : base(logger)
+        {
+        }
+
+        #region Overrides of DisposableObject
+
+        /// <inheritdoc />
+        protected override void OnDisposing()
+        {
+        }
+
+        #endregion
+
+        #region Overrides of BaseStorageProvider
+
+        /// <inheritdoc />
+        protected override string OnCreateItem(string id, string owner, string ownerRef, string providerClass, int initialAttempts,
+            DateTime created, string s)
+        {
+            return TestId;
+        }
+
+        /// <inheritdoc />
+        protected override void OnDeleteItem(string itemId)
+        {
+        }
+
+        /// <inheritdoc />
+        public override bool ContainsItem(string itemId)
+        {
+            return ReturnContainsItem;
+        }
+
+        #endregion
+    }
+}

+ 28 - 0
qdr.fnd.core.pqueue.test/qdr.fnd.core.pqueue.test.csproj

@@ -0,0 +1,28 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net7.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+
+    <IsPackable>false</IsPackable>
+    <IsTestProject>true</IsTestProject>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="coverlet.collector" Version="3.2.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
+    <PackageReference Include="NUnit" Version="3.13.3" />
+    <PackageReference Include="NUnit.Analyzers" Version="3.6.1" />
+    <PackageReference Include="NUnit3TestAdapter" Version="4.4.2" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\qdr.fnd.core.pqueue\qdr.fnd.core.pqueue.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Using Include="NUnit.Framework" />
+  </ItemGroup>
+
+</Project>

+ 143 - 1
qdr.fnd.core.pqueue/Configuration/PsQueueConfiguration.cs

@@ -1,6 +1,148 @@
-namespace qdr.fnd.core.pqueue.Configuration
+using qdr.fnd.core.pqueue.Storages;
+using Quadarax.Foundation.Core.Data;
+
+namespace qdr.fnd.core.pqueue.Configuration
 {
+    /// <summary>
+    /// Main PSQueue configuration object.
+    /// </summary>
     public class PsQueueConfiguration
     {
+        #region *** Properties ***
+        /// <summary>
+        /// Unique name of the queue, locally based on the application.
+        /// </summary>
+        public string QueueName { get; set; }
+        /// <summary>
+        /// Declare storage configuration for the queue.
+        /// </summary>
+        public PsQueueStorageConfiguration Storage { get; set; }
+        /// <summary>
+        /// Maximum number of concurrent processes that can be executed at once (How many threads uses).
+        /// </summary>
+        public int MaxConcurrentProcesses { get; set; }
+        /// <summary>
+        /// Defines the interval to check the queue for new items to process. If not specified, uses continuous strategy
+        /// </summary>
+        public TimeSpan? CycleInterval { get; set; }
+        /// <summary>
+        /// Default initial attempts for the queue items if not defined.
+        /// </summary>
+        public int? DefaultInitialAttempts { get; set; }
+        /// <summary>
+        /// Default process (one attempt) timeout for the queue items.
+        /// </summary>
+        public TimeSpan DefaultProcessTimeout { get; set; }
+        /// <summary>
+        /// Default attempt to postpone for the queue items when attempt failed. If not specified new attempt will be executed instantly
+        /// </summary>
+        public TimeSpan? DefaultAttemptPostpone { get; set; }
+        /// <summary>
+        /// Defines retention configuration for the queue.
+        /// </summary>
+        public PsQueueRetentionConfiguration? Retention { get; set; }
+        #endregion
+
+        #region *** Constructors ***
+
+        public PsQueueConfiguration() : this(string.Empty)
+        {
+        }
+        public PsQueueConfiguration(string queueName)
+        {
+            QueueName = queueName;
+            Storage = new PsQueueStorageConfiguration();
+        }
+        #endregion
+
+        #region *** Operations ***
+
+        /// <summary>
+        /// Create default configuration for the queue.
+        /// </summary>
+        /// <param name="queueName">Local unique queue name.</param>
+        /// <returns></returns>
+        public static PsQueueConfiguration Default(string queueName)
+        {
+            var config = new PsQueueConfiguration(queueName)
+            {
+                MaxConcurrentProcesses = 4,
+                CycleInterval = TimeSpan.FromSeconds(10),
+                DefaultInitialAttempts = 3,
+                DefaultProcessTimeout = TimeSpan.FromSeconds(30),
+                DefaultAttemptPostpone = TimeSpan.FromSeconds(30)
+            };
+
+            var storageProviderClass = typeof(MemoryStorageProvider).FullName;
+            if (storageProviderClass != null)
+                config.Storage.ProviderClass = storageProviderClass;
+
+            config.Retention = new PsQueueRetentionConfiguration
+            {
+                Enabled = true,
+                RetentionDoneOlder = TimeSpan.FromDays(7),
+                RetentionDoneMaxCount = 1000,
+                RetentionFailedOlder = TimeSpan.FromDays(7),
+                RetentionFailedMaxCount = 1000
+            };
+            return config;
+        }
+        #endregion
     }
+
+    /// <summary>
+    /// Storage configuration for the queue.
+    /// </summary>
+    public class PsQueueStorageConfiguration
+    {
+        /// <summary>
+        /// Qualified class name of the storage provider.
+        /// </summary>
+        public string ProviderClass { get; set; } = string.Empty;
+        /// <summary>
+        /// Storage source (path or connection string).
+        /// </summary>
+        public string Source { get; set; } = string.Empty;
+        /// <summary>
+        /// User identity for the storage provider or impersonation account.
+        /// </summary>
+        public string? UserName { get; set; }
+        /// <summary>
+        /// User password hashed by concat <see cref="PsQueueConfiguration.QueueName"/> and <see cref="UserName"/>.
+        /// </summary>
+        public string? UserPasswordHashed { get; set; }
+        /// <summary>
+        /// Provider specific configuration.
+        /// </summary>
+        public IList<TypedArgument>? Arguments { get; set; }
+        
+    }
+
+    /// <summary>
+    /// Retention configuration for the queue.
+    /// </summary>
+    public class PsQueueRetentionConfiguration
+    {
+        /// <summary>
+        /// Flag to enable retention configuration.
+        /// </summary>
+        public bool Enabled { get; set; }
+        /// <summary>
+        /// Maximum time to keep the queue item that are Done in the queue.
+        /// </summary>
+        public TimeSpan? RetentionDoneOlder { get; set; }
+        /// <summary>
+        /// Maximum queue items that are Done in the queue.
+        /// </summary>
+        public int? RetentionDoneMaxCount { get; set; }
+        /// <summary>
+        /// Maximum time to keep the queue item that are Failed in the queue.
+        /// </summary>
+        public TimeSpan? RetentionFailedOlder { get; set; }
+        /// <summary>
+        /// Maximum queue items that are Failed in the queue.
+        /// </summary>
+        public int? RetentionFailedMaxCount { get; set; }
+    }
+
 }

+ 43 - 0
qdr.fnd.core.pqueue/Exceptions/PSQueueException.cs

@@ -0,0 +1,43 @@
+using Quadarax.Foundation.Core.Attributes;
+using Quadarax.Foundation.Core.Exceptions;
+
+namespace qdr.fnd.core.pqueue.Exceptions
+{
+    public class PSQueueException: CodeException<PSQueueException.ErrorCodes>
+    {
+
+        #region *** Error Codes ***
+
+        public enum ErrorCodes : int
+        {
+            [ErrorMessage("Cannot start, queue is already running.")]
+            PsQueueIsRunning = 5000,
+            [ErrorMessage("Cannot stop, queue is not running.")]
+            PsQueueIsNotRunning = 5001,
+        }
+        #endregion
+
+        #region *** Constructors ***
+
+        /// <inheritdoc />
+        public PSQueueException(ErrorCodes code, Exception innerException) : base(code, innerException)
+        {
+        }
+
+        /// <inheritdoc />
+        public PSQueueException(ErrorCodes code) : base(code)
+        {
+        }
+
+        /// <inheritdoc />
+        public PSQueueException(ErrorCodes code, params object[] messageArguments) : base(code, messageArguments)
+        {
+        }
+
+        /// <inheritdoc />
+        public PSQueueException(ErrorCodes code, Exception innerException, params object[] messageArguments) : base(code, innerException, messageArguments)
+        {
+        }
+        #endregion
+    }
+}

+ 47 - 0
qdr.fnd.core.pqueue/Exceptions/PSStorageException.cs

@@ -0,0 +1,47 @@
+using Quadarax.Foundation.Core.Attributes;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Quadarax.Foundation.Core.Exceptions;
+
+namespace qdr.fnd.core.pqueue.Exceptions
+{
+    public class PSStorageException : CodeException<PSStorageException.ErrorCodes>
+    {
+        #region *** Error Codes ***
+
+        public enum ErrorCodes : int
+        {
+            [ErrorMessage("Item with id={0} not found in storage.")]
+            PsStorageItemNotFound = 6000,
+            [ErrorMessage("Item with id={0} already exists in storage.")]
+            PsStorageItemAlreadyExists = 6001,
+        }
+        #endregion
+
+        #region *** Constructors ***
+
+        /// <inheritdoc />
+        public PSStorageException(ErrorCodes code, Exception innerException) : base(code, innerException)
+        {
+        }
+
+        /// <inheritdoc />
+        public PSStorageException(ErrorCodes code) : base(code)
+        {
+        }
+
+        /// <inheritdoc />
+        public PSStorageException(ErrorCodes code, params object[] messageArguments) : base(code, messageArguments)
+        {
+        }
+
+        /// <inheritdoc />
+        public PSStorageException(ErrorCodes code, Exception innerException, params object[] messageArguments) : base(code, innerException, messageArguments)
+        {
+        }
+        #endregion
+    }
+}

+ 17 - 0
qdr.fnd.core.pqueue/IPsQueue.cs

@@ -3,12 +3,29 @@
     public interface IPsQueue : IDisposable
     {
         #region *** Properties ***
+        /// <summary>
+        /// Flag determines if the queue is running. Means if <see cref="Start"/> was called and <see cref="Stop"/> was not called.
+        /// </summary>
         bool IsRunning { get; }
         #endregion
 
         #region *** Operations ***
+        /// <summary>
+        /// Starts the queue engine to processing queue items. If the queue is already running, the method throws an exception.
+        /// <exception cref="CodeExeption"></exception>
+        /// </summary>
         void Start();
         void Stop();
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="item"></param>
+        /// <returns></returns>
+        string EnQueue(IPsQueueItemDescriptor item);
+
+        bool DeQueue(string itemId, bool force);
+
         #endregion
     }
 }

+ 34 - 9
qdr.fnd.core.pqueue/IPsQueueItem.cs

@@ -3,25 +3,50 @@ using Quadarax.Foundation.Core.Data;
 
 namespace qdr.fnd.core.pqueue
 {
-    public interface IPsQueueItem
+    /// <summary>
+    /// Interface for the queue item.
+    /// </summary>
+    public interface IPsQueueItem : IPsQueueItemDescriptor
     {
         #region *** Properties ***
+        /// <summary>
+        /// Unique identifier of the queue item.
+        /// </summary>
         string Id { get; }
-        public string Owner { get; }
-        public string OwnerRef { get; }
-        int Priority { get; }
-        DateTime ValidFrom { get; }
-        DateTime? ValidTo { get; }
+        
+        /// <summary>
+        /// Date and time when the queue item was created.
+        /// </summary>
         DateTime Created { get; }
+        /// <summary>
+        /// Date and time when the queue item was started to process (last cycle).
+        /// </summary>
         DateTime? Started { get; }
+        /// <summary>
+        /// Date and time when the queue item was finished to process (last cycle).
+        /// </summary>
         DateTime? Finished { get; }
+        /// <summary>
+        /// Process item current status. Changes are logged in <see cref="StatusJournal"/>.
+        /// </summary>
         PsStatusEnum Status { get; }
+
+        /// <summary>
+        /// Defines if <see cref="Status"/> is transient state. Changes are logged in <see cref="StatusJournal"/>.
+        /// </summary>
         bool IsStatusTransient { get; }
+        /// <summary>
+        /// Defines current number of attempts left to process the queue item.
+        /// </summary>
         int AttemptsLeft { get; }
-        int AtteptsInitial { get; }
-        string ProcessProviderClass { get; }
-        IEnumerable<ITypedArgument> Arguments { get; }
+        
+        /// <summary>
+        /// Collection of messages, errors, warnings logged during the process.
+        /// </summary>
         IEnumerable<IError> Log { get; }
+        /// <summary>
+        /// Collection of status changes of the queue item. When <see cref="Status"/> or <see cref="IsStatusTransient"/> is changed, the status change is logged.
+        /// </summary>
         IEnumerable<PsStatusHistory> StatusJournal { get;}
         #endregion
     }

+ 44 - 0
qdr.fnd.core.pqueue/IPsQueueItemDescriptor.cs

@@ -0,0 +1,44 @@
+using Quadarax.Foundation.Core.Data;
+
+namespace qdr.fnd.core.pqueue
+{
+    public interface IPsQueueItemDescriptor
+    {
+        /// <summary>
+        /// Holds information about the owner (type of owner) of the queue item.
+        /// </summary>
+        public string Owner { get; }
+        /// <summary>
+        /// Holds information about the owner identifier (reference number).
+        /// </summary>
+        public string OwnerRef { get; }
+        /// <summary>
+        /// Priority of the queue item. Lower number means higher priority.
+        /// </summary>
+        int Priority { get; }
+        
+        /// <summary>
+        /// Date and time when the queue item will be possibly processed.
+        /// </summary>
+        DateTime ValidFrom { get; }
+
+        /// <summary>
+        /// Date and time when the queue item will be expired. If null, the queue item will never expire.
+        /// </summary>
+        DateTime? ValidTo { get; }
+
+        /// <summary>
+        /// Defines initial number of attempts left to process the queue item. When item will be reset uses this value.
+        /// </summary>
+        int AtteptsInitial { get; }
+
+        /// <summary>
+        /// Class name of the process executing provider.
+        /// </summary>
+        string ProcessProviderClass { get; }
+        /// <summary>
+        /// Collection of input and output attributes of current process.
+        /// </summary>
+        IEnumerable<ITypedArgument> Arguments { get; }
+    }
+}

+ 3 - 0
qdr.fnd.core.pqueue/Program.cs

@@ -1,2 +1,5 @@
 // See https://aka.ms/new-console-template for more information
 Console.WriteLine("Hello, World!");
+
+
+

+ 14 - 21
qdr.fnd.core.pqueue/PsQueue.cs

@@ -1,5 +1,6 @@
 using qdr.fnd.core.pqueue.Configuration;
 using qdr.fnd.core.pqueue.Enums;
+using qdr.fnd.core.pqueue.Exceptions;
 using qdr.fnd.core.pqueue.Process;
 using qdr.fnd.core.pqueue.Storages;
 using Quadarax.Foundation.Core.Data;
@@ -45,60 +46,52 @@ namespace qdr.fnd.core.pqueue
         #region *** Public Operations ***
         public void Start()
         {
-            //TODO: Implement Start
+            if (IsRunning) throw new PSQueueException(PSQueueException.ErrorCodes.PsQueueIsRunning);
+            _log.Log(LogSeverityEnum.Info, $"{Name}:queue started.");
         }
         public void Stop()
         {
-            //TODO: Implement Stop
+            if (!IsRunning) throw new PSQueueException(PSQueueException.ErrorCodes.PsQueueIsRunning);
+            _log.Log(LogSeverityEnum.Info, $"{Name}:queue stopped.");
         }
 
-        public IPsQueueItem Queue(string owner, string ownerRef, IPsProcessProvider provider, IEnumerable<ITypedArgument> arguments, int priority,int initialAttepts, DateTime validFrom, DateTime? validTo)
+        public string EnQueue(IPsQueueItemDescriptor item)
         {
-
-        }
-        public IPsQueueItem Queue(string owner, string ownerRef, IPsProcessProvider provider, IEnumerable<ITypedArgument> arguments, int priority,int initialAttepts)
-        {
-
-        }
-
-        public IPsQueueItem Queue(string owner, string ownerRef, IPsProcessProvider provider, IEnumerable<ITypedArgument> arguments)
-        {
-
+            throw new NotImplementedException();
         }
-
         public bool Dequeue(string itemId, bool force)
         {
-
+            throw new NotImplementedException();
         }
 
         public IQueryable<IPsQueueItem> Query(string? owner, string? ownerRef, params Tuple<bool,PsStatusEnum>[] status)
         {
-
+            throw new NotImplementedException();
         }
 
         public IPsQueueItem? Get(string itemId)
         {
-
+            throw new NotImplementedException();
         }
 
         public bool Postpone(string itemId, TimeSpan postpone)
         {
-
+            throw new NotImplementedException();
         }
 
         public bool Reset(string itemId)
         {
-
+            throw new NotImplementedException();
         }
 
         public bool SetPriority(string itemId, int priority)
         {
-
+            throw new NotImplementedException();
         }
 
         public IEnumerable<IPsQueueItem> GetWorkingItems()
         {
-
+            throw new NotImplementedException();
         }
         #endregion
 

+ 84 - 0
qdr.fnd.core.pqueue/PsQueueItem.cs

@@ -0,0 +1,84 @@
+using qdr.fnd.core.pqueue.Enums;
+using Quadarax.Foundation.Core.Data;
+
+namespace qdr.fnd.core.pqueue
+{
+    public class PsQueueItem : IPsQueueItem
+    {
+        #region *** Properties ***
+
+        /// <inheritdoc />
+        public string Id { get; protected set; }
+
+        /// <inheritdoc />
+        public string Owner { get; protected set; }
+
+        /// <inheritdoc />
+        public string OwnerRef { get; protected set; }
+
+        /// <inheritdoc />
+        public int Priority { get; internal set; }
+
+        /// <inheritdoc />
+        public DateTime ValidFrom { get; internal set; }
+
+        /// <inheritdoc />
+        public DateTime? ValidTo { get; internal set; }
+
+        /// <inheritdoc />
+        public DateTime Created { get; protected set; }
+
+        /// <inheritdoc />
+        public DateTime? Started { get; internal set; }
+
+        /// <inheritdoc />
+        public DateTime? Finished { get; internal set; }
+
+        /// <inheritdoc />
+        public PsStatusEnum Status { get; internal set; }
+
+        /// <inheritdoc />
+        public bool IsStatusTransient { get; internal set; }
+
+        /// <inheritdoc />
+        public int AttemptsLeft { get; internal set; }
+
+        /// <inheritdoc />
+        public int AtteptsInitial { get; internal set; }
+
+        /// <inheritdoc />
+        public string ProcessProviderClass { get; protected set; }
+
+        /// <inheritdoc />
+        public IEnumerable<ITypedArgument> Arguments { get; protected set; }
+
+        /// <inheritdoc />
+        public IEnumerable<IError> Log { get;  protected set;}
+
+        /// <inheritdoc />
+        public IEnumerable<PsStatusHistory> StatusJournal { get; protected set; }
+
+        #endregion
+        #region *** Constructors ***
+
+        public PsQueueItem(string id) : this(id, string.Empty, string.Empty, string.Empty, DateTime.Now)
+        {
+        }
+        public PsQueueItem(string id, string owner, string ownerRef, string providerClass, DateTime created)
+        {
+            Id = id;
+            Status = PsStatusEnum.New;
+            IsStatusTransient = false;
+            Owner = owner;
+            OwnerRef = ownerRef;
+            ProcessProviderClass = providerClass;
+            Created = created;
+
+            Arguments = new List<ITypedArgument>();
+            Log = new List<IError>();
+            StatusJournal = new List<PsStatusHistory>();
+        }
+        #endregion
+
+    }
+}

+ 43 - 75
qdr.fnd.core.pqueue/Storages/BaseStorageProvider.cs

@@ -1,5 +1,5 @@
-using System.Collections.Concurrent;
-using qdr.fnd.core.pqueue.Enums;
+using qdr.fnd.core.pqueue.Enums;
+using qdr.fnd.core.pqueue.Exceptions;
 using Quadarax.Foundation.Core.Data;
 using Quadarax.Foundation.Core.Logging;
 using Quadarax.Foundation.Core.Object;
@@ -9,17 +9,15 @@ namespace qdr.fnd.core.pqueue.Storages
 {
     public abstract class BaseStorageProvider : DisposableObject, IPsQueueStorage
     {
-
+        
         #region *** Properties ***
+        protected ILog? Log => _log;
         #endregion
 
         #region *** Private Fields ***
 
-        private IDictionary<string, IList<IPsQueueItem>> _tranScopes =
-            new ConcurrentDictionary<string, IList<IPsQueueItem>>();
-
         private ILog? _log;
-        private IIdGenerator<string>? _tranGenerator;
+        private IIdGenerator<string> _tranGenerator;
 
         #endregion
 
@@ -32,91 +30,94 @@ namespace qdr.fnd.core.pqueue.Storages
 
         #endregion
 
-        #region *** Operations ***
-        #region **** Transaction ****
 
-        /// <inheritdoc />
-        public string CreateTransaction()
-        {
-            throw new NotImplementedException();
-        }
+        #region Implementation of IPsQueueStorage
 
         /// <inheritdoc />
-        public void CommitTransaction(string transactionId)
+        public string CreateItem(string owner, string ownerRef, string providerClass, int initialAttempts, DateTime? created)
         {
-            throw new NotImplementedException();
-        }
+            if (string.IsNullOrWhiteSpace(owner))
+                throw new ArgumentNullException(nameof(owner));
+            if (string.IsNullOrWhiteSpace(ownerRef))
+                throw new ArgumentNullException(nameof(ownerRef));
+            if (string.IsNullOrWhiteSpace(providerClass))
+                throw new ArgumentNullException(nameof(providerClass));
+            if (created == null)
+                created = DateTime.Now;
 
-        /// <inheritdoc />
-        public void RollbackTransaction(string transactionId)
-        {
-            throw new NotImplementedException();
-        }
-        #endregion
+            var id = _tranGenerator.NewId();
 
-        /// <inheritdoc />
-        public string CreateItem(string transactionId, string owner, string ownerRef, string providerClass, DateTime? created)
-        {
-            throw new NotImplementedException();
+            var newId = OnCreateItem(id, owner, ownerRef, providerClass, initialAttempts, created.Value, id);
+
+            _log?.Log(LogSeverityEnum.Trace, $"Item [id={id}, owner={owner}, ownerRef{ownerRef}] created");
+            return newId;
         }
 
+        protected abstract string OnCreateItem(string id, string owner, string ownerRef, string providerClass, int initialAttempts, DateTime created, string s);
+
         /// <inheritdoc />
-        public void DeleteItem(string transactionId, string itemId)
+        public void DeleteItem(string itemId)
         {
-            throw new NotImplementedException();
+            if (string.IsNullOrWhiteSpace(itemId))
+                throw new ArgumentNullException(nameof(itemId));
+            if (!ContainsItem(itemId))
+                throw new PSStorageException(PSStorageException.ErrorCodes.PsStorageItemNotFound, itemId);
+
+            OnDeleteItem(itemId);
         }
 
+        protected abstract void OnDeleteItem(string itemId);
+
         /// <inheritdoc />
-        public void UpdateItemPriority(string transactionId, string itemId, int priority)
+        public void UpdateItemPriority(string itemId, int priority)
         {
             throw new NotImplementedException();
         }
 
         /// <inheritdoc />
-        public void UpdateItemValidity(string transactionId, string itemId, DateTime validForm, DateTime? validTo)
+        public void UpdateItemValidity(string itemId, DateTime validForm, DateTime? validTo)
         {
             throw new NotImplementedException();
         }
 
         /// <inheritdoc />
-        public void UpdateItemStatus(string transactionId, string itemId, PsStatusEnum? status, bool isTransient)
+        public void UpdateItemStatus(string itemId, PsStatusEnum? status, bool isTransient)
         {
             throw new NotImplementedException();
         }
 
         /// <inheritdoc />
-        public void UpdateItemAttempts(string transactionId, string itemId, int attemptsLeft)
+        public void UpdateItemAttempts(string itemId, int attemptsLeft)
         {
             throw new NotImplementedException();
         }
 
         /// <inheritdoc />
-        public void UpdateItemStarted(string transactionId, string itemId, DateTime? started)
+        public void UpdateItemStarted(string itemId, DateTime? started)
         {
             throw new NotImplementedException();
         }
 
         /// <inheritdoc />
-        public void UpdateItemFinished(string transactionId, string itemId, DateTime? finished)
+        public void UpdateItemFinished(string itemId, DateTime? finished)
         {
             throw new NotImplementedException();
         }
 
         /// <inheritdoc />
-        public void CreateItemAttribute(string transactionId, string itemId, string name, string? value, string valueTypeClass,
-            bool isOutput)
+        public void CreateItemAttribute(string itemId, string name, string? value, string valueTypeClass, bool isOutput)
         {
             throw new NotImplementedException();
         }
 
         /// <inheritdoc />
-        public void UpdateItemAttribute(string transactionId, string itemId, string name, string value)
+        public void UpdateItemAttribute(string itemId, string name, string value)
         {
             throw new NotImplementedException();
         }
 
         /// <inheritdoc />
-        public void DeleteItemAttribute(string transactionId, string itemId, string name)
+        public void DeleteItemAttribute(string itemId, string name)
         {
             throw new NotImplementedException();
         }
@@ -128,7 +129,7 @@ namespace qdr.fnd.core.pqueue.Storages
         }
 
         /// <inheritdoc />
-        public void AppendStatusJournal(string transactionId, string itemId, PsStatusEnum status, bool isTransient, string message)
+        public void AppendStatusJournal(string itemId, PsStatusEnum status, bool isTransient, string message)
         {
             throw new NotImplementedException();
         }
@@ -140,46 +141,13 @@ namespace qdr.fnd.core.pqueue.Storages
         }
 
         /// <inheritdoc />
-        public IPsQueueItem GetItem(string? transactionId, string itemId)
+        public IPsQueueItem GetItem(string itemId)
         {
             throw new NotImplementedException();
         }
 
-        #endregion
-        #region *** Private Operations ***
-
-        #region Overrides of DisposableObject
-
         /// <inheritdoc />
-        protected override void OnDisposing()
-        {
-            // rollback all transactions
-            foreach (var tranId in _tranScopes.Keys.ToArray())
-            {
-                if(_tranScopes.ContainsKey(tranId))
-                    RollbackTransaction(tranId);
-            }
-            // cleanup rest of transaction scopes
-            _tranScopes.Clear();
-
-            // cleanup logger
-            _log = null;
-
-            // cleanup id generator
-            _tranGenerator = null;
-        }
-
-        protected void Log(LogSeverityEnum severity, string? transactionId, string itemId, string message, Exception? ex = null)
-        {
-            if (!string.IsNullOrWhiteSpace(transactionId))
-                message = $"[TRX: {transactionId}; ID: {itemId}] {message}";
-            _log?.Log(severity, message, ex);
-        }
-        protected void Log(LogSeverityEnum severity, string message, Exception? ex = null)
-        {
-            Log(severity, null, string.Empty, message, ex);
-        }
-        #endregion
+        public abstract bool ContainsItem(string itemId);
 
         #endregion
     }

+ 22 - 61
qdr.fnd.core.pqueue/Storages/IPsQueueStorage.cs

@@ -13,130 +13,92 @@ namespace qdr.fnd.core.pqueue.Storages
     public interface IPsQueueStorage : IDisposable
     {
         #region *** Operations ***
-
-        #region **** Transactions ****
-        /// <summary>
-        /// Creates a new transaction and returns its id
-        /// </summary>
-        /// <returns>Transaction identifier</returns>
-        public string CreateTransaction();
-        /// <summary>
-        /// Commits all changes on the transaction specified by the <paramref name="transactionId"/> and closes transaction.
-        /// <remarks>
-        /// If <paramref name="transactionId"/> not exists, the method should throw an exception.
-        /// </remarks>
-        /// </summary>
-        /// <param name="transactionId">Transaction identifier</param>
-        public void CommitTransaction(string transactionId);
-        /// <summary>
-        /// Rollbacks all changes on the transaction specified by the <paramref name="transactionId"/> and closes transaction.
-        /// <remarks>
-        /// If <paramref name="transactionId"/> not exists, the method should throw an exception.
-        /// </remarks>
-        /// </summary>
-        /// <param name="transactionId">Transaction identifier</param>
-        public void RollbackTransaction(string transactionId);
-        #endregion
         
         #region **** Items ****
+
         /// <summary>
         /// Creates a new item in the queue storage and returns its identifier.
         /// <remarks>
-        /// If <paramref name="transactionId"/> not exists, the method should throw an exception.
         /// </remarks>
         /// </summary>
-        /// <param name="transactionId">Transaction identifier generated by <see cref="CreateTransaction"/> operation.</param>
         /// <param name="owner">Owner identifier</param>
         /// <param name="ownerRef">Owner reference identifier</param>
         /// <param name="providerClass">Process provider qualified type name</param>
+        /// <param name="initialAttempts">Initial amount of attempts to processing items</param>
         /// <param name="created">Timestamp when record will be created. If null use current datetime.</param>
         /// <returns>Item identifier.</returns>
-        public string CreateItem(string transactionId, string owner, string ownerRef, string providerClass, DateTime? created);
+        public string CreateItem(string owner, string ownerRef, string providerClass,int initialAttempts, DateTime? created);
         /// <summary>
         /// Deletes (force) the item (and all related entities) specified by the <paramref name="itemId"/> no matter what status item has.
         /// <remarks>
-        /// If <paramref name="transactionId"/> not exists, the method should throw an exception.
         /// If <paramref name="itemId"/> not exists, the method should throw an exception.
         /// </remarks>
         /// </summary>
-        /// <param name="transactionId">Transaction identifier generated by <see cref="CreateTransaction"/> operation.</param>
         /// <param name="itemId">Item identifier.</param>
-        public void DeleteItem(string transactionId, string itemId);
+        public void DeleteItem(string itemId);
         /// <summary>
         /// Updates the priority of the item specified by the <paramref name="itemId"/>.
         /// <remarks>
-        /// If <paramref name="transactionId"/> not exists, the method should throw an exception.
         /// If <paramref name="itemId"/> not exists, the method should throw an exception.
         /// </remarks>
         /// </summary>
-        /// <param name="transactionId">Transaction identifier generated by <see cref="CreateTransaction"/> operation.</param>
         /// <param name="itemId">Item identifier.</param>
         /// <param name="priority">Process priority value.</param>
-        public void UpdateItemPriority(string transactionId, string itemId, int priority);
+        public void UpdateItemPriority(string itemId, int priority);
         
         /// <summary>
         /// Updates the validity of the item specified by the <paramref name="itemId"/>.
         /// <remarks>
-        /// If <paramref name="transactionId"/> not exists, the method should throw an exception.
         /// If <paramref name="itemId"/> not exists, the method should throw an exception.
         /// </remarks>
         /// </summary>
-        /// <param name="transactionId">Transaction identifier generated by <see cref="CreateTransaction"/> operation.</param>
         /// <param name="itemId">Item identifier.</param>
         /// <param name="validForm">Timestamp for validFrom value.</param>
         /// <param name="validTo">Timespamp for validTo value.</param>
-        public void UpdateItemValidity(string transactionId, string itemId, DateTime validForm, DateTime? validTo);
+        public void UpdateItemValidity(string itemId, DateTime validForm, DateTime? validTo);
         
         /// <summary>
         /// Updates the status of the item specified by the <paramref name="itemId"/>. Doesn't affects the status journal or logs.
         /// <remarks>
-        /// If <paramref name="transactionId"/> not exists, the method should throw an exception.
         /// If <paramref name="itemId"/> not exists, the method should throw an exception.
         /// Doesn't validate the status transitions. Force update.
         /// </remarks>
         /// </summary>
-        /// <param name="transactionId">Transaction identifier generated by <see cref="CreateTransaction"/> operation.</param>
         /// <param name="itemId">Item identifier.</param>
         /// <param name="status">New item status. If null status will be kept.</param>
         /// <param name="isTransient">New transient flag.</param>
-        public void UpdateItemStatus(string transactionId, string itemId, PsStatusEnum? status, bool isTransient);
+        public void UpdateItemStatus(string itemId, PsStatusEnum? status, bool isTransient);
         
         /// <summary>
         /// Updates the attempts left value of the item specified by the <paramref name="itemId"/>.
         /// <remarks>
-        /// If <paramref name="transactionId"/> not exists, the method should throw an exception.
         /// If <paramref name="itemId"/> not exists, the method should throw an exception.
         /// Doesn't validate attempts counter. Force update.
         /// </remarks>
         /// </summary>
-        /// <param name="transactionId">Transaction identifier generated by <see cref="CreateTransaction"/> operation.</param>
         /// <param name="itemId">Item identifier.</param>
         /// <param name="attemptsLeft">New attempt value.</param>
-        public void UpdateItemAttempts(string transactionId, string itemId, int attemptsLeft);
+        public void UpdateItemAttempts(string itemId, int attemptsLeft);
         
         /// <summary>
         /// Updates the process started timestamp of the item specified by the <paramref name="itemId"/>.
         /// <remarks>
-        /// If <paramref name="transactionId"/> not exists, the method should throw an exception.
         /// If <paramref name="itemId"/> not exists, the method should throw an exception.
         /// </remarks>
         /// </summary>
-        /// <param name="transactionId">Transaction identifier generated by <see cref="CreateTransaction"/> operation.</param>
         /// <param name="itemId">Item identifier.</param>
         /// <param name="started">New timestamp value or null.</param>
-        public void UpdateItemStarted(string transactionId, string itemId, DateTime? started);
+        public void UpdateItemStarted(string itemId, DateTime? started);
         
         /// <summary>
         /// Updates the process finished timestamp of the item specified by the <paramref name="itemId"/>.
         /// <remarks>
-        /// If <paramref name="transactionId"/> not exists, the method should throw an exception.
         /// If <paramref name="itemId"/> not exists, the method should throw an exception.
         /// </remarks>
         /// </summary>
-        /// <param name="transactionId">Transaction identifier generated by <see cref="CreateTransaction"/> operation.</param>
         /// <param name="itemId">Item identifier.</param>
         /// <param name="finished">New timestamp value or null.</param>
-        public void UpdateItemFinished(string transactionId, string itemId, DateTime? finished);
+        public void UpdateItemFinished(string itemId, DateTime? finished);
 
         #endregion
         #region **** Item Attributes ****
@@ -144,18 +106,16 @@ namespace qdr.fnd.core.pqueue.Storages
         /// <summary>
         /// Creates a new item attribute with the specified <paramref name="name"/> and <paramref name="value"/> for the item specified by the <paramref name="itemId"/>.
         /// <remarks>
-        /// If <paramref name="transactionId"/> not exists, the method should throw an exception.
         /// If <paramref name="itemId"/> not exists, the method should throw an exception.
         /// If <paramref name="name"/> already exists, the method should throw an exception.
         /// </remarks>
         /// </summary>
-        /// <param name="transactionId">Transaction identifier generated by <see cref="CreateTransaction"/> operation.</param>
         /// <param name="itemId">Item identifier.</param>
         /// <param name="name">Unique attribute name in <paramref name="itemId"/> scope.</param>
         /// <param name="value">Value serialized as string or null.</param>
         /// <param name="valueTypeClass">Value type qualified class name.</param>
         /// <param name="isOutput">Flag defines if attribute is input/output.</param>
-        public void CreateItemAttribute(string transactionId, string itemId, string name, string? value, string valueTypeClass,bool isOutput);
+        public void CreateItemAttribute(string itemId, string name, string? value, string valueTypeClass,bool isOutput);
         
         /// <summary>
         /// Updates the value of the item attribute specified by the <paramref name="name"/> for the item specified by the <paramref name="itemId"/>.
@@ -169,20 +129,18 @@ namespace qdr.fnd.core.pqueue.Storages
         /// <param name="itemId">Item identifier.</param>
         /// <param name="name">Unique attribute name in <paramref name="itemId"/> scope.</param>
         /// <param name="value">Value serialized as string or null.</param>
-        public void UpdateItemAttribute(string transactionId, string itemId, string name, string value);
+        public void UpdateItemAttribute(string itemId, string name, string value);
 
         /// <summary>
         /// Deletes the item attribute specified by the <paramref name="name"/> for the item specified by the <paramref name="itemId"/>.
         /// <remarks>
-        /// If <paramref name="transactionId"/> not exists, the method should throw an exception.
         /// If <paramref name="itemId"/> not exists, the method should throw an exception.
         /// If <paramref name="name"/> not exists, the method should throw an exception.
         /// </remarks>
         /// </summary>
-        /// <param name="transactionId">Transaction identifier generated by <see cref="CreateTransaction"/> operation.</param>
         /// <param name="itemId">Item identifier.</param>
         /// <param name="name">Unique attribute name in <paramref name="itemId"/> scope.</param>
-        public void DeleteItemAttribute(string transactionId, string itemId, string name);
+        public void DeleteItemAttribute(string itemId, string name);
 
         #endregion
         #region **** Item Collections ****
@@ -202,16 +160,14 @@ namespace qdr.fnd.core.pqueue.Storages
         /// <summary>
         /// Appends a new status journal record to the item specified by the <paramref name="itemId"/>.
         /// <remarks>
-        /// If <paramref name="transactionId"/> not exists, the method should throw an exception.
         /// If <paramref name="itemId"/> not exists, the method should throw an exception.
         /// </remarks>
         /// </summary>
-        /// <param name="transactionId">Transaction identifier generated by <see cref="CreateTransaction"/> operation.</param>
         /// <param name="itemId">Item identifier.</param>
         /// <param name="status">Previous status.</param>
         /// <param name="isTransient">Previous isTransient flag.</param>
         /// <param name="message">Message.</param>
-        public void AppendStatusJournal(string transactionId, string itemId, PsStatusEnum status, bool isTransient, string message);
+        public void AppendStatusJournal(string itemId, PsStatusEnum status, bool isTransient, string message);
         #endregion
 
         #region **** Queries & Get ****
@@ -230,14 +186,19 @@ namespace qdr.fnd.core.pqueue.Storages
         /// <summary>
         /// Gets the item specified by the <paramref name="itemId"/>.
         /// <remarks>
-        /// If <paramref name="transactionId"/> not exists, the method should throw an exception.
         /// If <paramref name="itemId"/> not exists, the method should throw an exception.
         /// </remarks>
         /// </summary>
-        /// <param name="transactionId">Transaction identifier generated by <see cref="CreateTransaction"/> operation. If null works outside transaction scope.</param>
         /// <param name="itemId">Item identifier.</param>
         /// <returns>Requested item.</returns>
-        public IPsQueueItem GetItem(string? transactionId, string itemId);
+        public IPsQueueItem GetItem(string itemId);
+
+        /// <summary>
+        /// Checks if the item with the specified <paramref name="itemId"/> exists in the storage.
+        /// </summary>
+        /// <param name="itemId">Item identifier.</param>
+        /// <returns>Returns true if item exists in storage.</returns>
+        public bool ContainsItem(string itemId);
         #endregion
         #endregion
     }

+ 66 - 0
qdr.fnd.core.pqueue/Storages/MemoryStorageProvider.cs

@@ -0,0 +1,66 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Quadarax.Foundation.Core.Logging;
+
+namespace qdr.fnd.core.pqueue.Storages
+{
+    public class MemoryStorageProvider : BaseStorageProvider
+    {
+
+        private ConcurrentDictionary<string, IPsQueueItem> _items = new ConcurrentDictionary<string, IPsQueueItem>();
+
+        /// <inheritdoc />
+        public MemoryStorageProvider(ILogger? logger) : base(logger)
+        {
+        }
+
+        #region Overrides of BaseStorageProvider
+
+        #endregion
+
+        #region Overrides of DisposableObject
+
+        /// <inheritdoc />
+        protected override void OnDisposing()
+        {
+            _items.Clear();
+            _items = null!;
+        }
+
+        #endregion
+
+        #region Overrides of BaseStorageProvider
+
+        /// <inheritdoc />
+        protected override string OnCreateItem(string id, string owner, string ownerRef, string providerClass, int initialAttempts,
+            DateTime created, string s)
+        {
+            var item = new PsQueueItem(id, owner, ownerRef, providerClass, created)
+            {
+                AttemptsLeft = initialAttempts
+            };
+            if(!_items.TryAdd(id, item))
+                Log?.Log(LogSeverityEnum.Warn, $"Item id={id} already exists in storage (id conflict)");
+            return id;
+        }
+
+        /// <inheritdoc />
+        protected override void OnDeleteItem(string itemId)
+        {
+            if(_items.TryRemove(itemId, out _))
+                Log?.Log(LogSeverityEnum.Warn, $"Item id={itemId} wasn't delete from storage (not exists)");
+        }
+
+        /// <inheritdoc />
+        public override bool ContainsItem(string itemId)
+        {
+            return _items.ContainsKey(itemId);
+        }
+
+        #endregion
+    }
+}

+ 2 - 2
qdr.fnd.core.pqueue/qdr.fnd.core.pqueue.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
@@ -8,7 +8,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="qdr.fnd.core" Version="0.0.3-alpha" />
+    <PackageReference Include="qdr.fnd.core" Version="0.0.4-alpha" />
   </ItemGroup>
 
 </Project>

+ 12 - 1
qdr.fnd.core.pqueue/qdr.fnd.core.pqueue.sln

@@ -3,7 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio Version 17
 VisualStudioVersion = 17.8.34525.116
 MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "qdr.fnd.core.pqueue", "qdr.fnd.core.pqueue.csproj", "{317F61E7-0FEB-4863-BA0E-4217A558D224}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "qdr.fnd.core.pqueue", "qdr.fnd.core.pqueue.csproj", "{317F61E7-0FEB-4863-BA0E-4217A558D224}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{0625F4F3-6215-4ECA-A57C-1E769B368506}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "qdr.fnd.core.pqueue.test", "..\qdr.fnd.core.pqueue.test\qdr.fnd.core.pqueue.test.csproj", "{086AEE9D-D8AA-48F1-8256-C786C45EAA25}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -15,10 +19,17 @@ Global
 		{317F61E7-0FEB-4863-BA0E-4217A558D224}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{317F61E7-0FEB-4863-BA0E-4217A558D224}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{317F61E7-0FEB-4863-BA0E-4217A558D224}.Release|Any CPU.Build.0 = Release|Any CPU
+		{086AEE9D-D8AA-48F1-8256-C786C45EAA25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{086AEE9D-D8AA-48F1-8256-C786C45EAA25}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{086AEE9D-D8AA-48F1-8256-C786C45EAA25}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{086AEE9D-D8AA-48F1-8256-C786C45EAA25}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 	EndGlobalSection
+	GlobalSection(NestedProjects) = preSolution
+		{086AEE9D-D8AA-48F1-8256-C786C45EAA25} = {0625F4F3-6215-4ECA-A57C-1E769B368506}
+	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {8CF77B44-3034-43BC-9964-E1A956F0CEF7}
 	EndGlobalSection

+ 2 - 1
readme.txt

@@ -1,9 +1,10 @@
 Contains all sources for Quadarax.Foundation (QDR.FND).
 -------------------------------------------------------
-Last Update: 1.12.2019
+Last Update: 14.3.2024
 
 Folder List:
 Assets							QDR.FND.Assets							* Contains QDR graphics, icons, bitmaps, logos, certs
 QConsole						QDR.FND.QConsole						* Simple console framework for custom console application
 Common							QDR.FND.Common							* Simple common patterns and extensions
+Core							QDR.FND.Core							* Contains basic structures, patterns and helpers. This is main base library.