From a0b98d2848090d83730a84aa7539d9c235316139 Mon Sep 17 00:00:00 2001
From: onepiecefreak3 <alberteinstein1980@gmx.de>
Date: Thu, 19 Sep 2019 14:26:14 +0200
Subject: [PATCH] Add QueryAll functionality;

---
 .../ClientQueryAllEnumerator.cs               | 97 +++++++++++++++++++
 .../ClientQueryProvider.cs                    | 14 ++-
 .../ClientQueryableCollection.cs              |  4 +-
 .../LinqExtensions.cs                         |  5 +
 4 files changed, 117 insertions(+), 3 deletions(-)
 create mode 100644 commercetools.Sdk/commercetools.Sdk.Client/ClientQueryAllEnumerator.cs

diff --git a/commercetools.Sdk/commercetools.Sdk.Client/ClientQueryAllEnumerator.cs b/commercetools.Sdk/commercetools.Sdk.Client/ClientQueryAllEnumerator.cs
new file mode 100644
index 00000000..af1aa6ff
--- /dev/null
+++ b/commercetools.Sdk/commercetools.Sdk.Client/ClientQueryAllEnumerator.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using commercetools.Sdk.Domain;
+
+namespace commercetools.Sdk.Client
+{
+    public class ClientQueryAllEnumerator<TCommand> : IEnumerator<TCommand>
+    {
+        private const int DefaultOffset = 0;
+        private const int DefaultLimit = 20;
+
+        private IClient client;
+        private QueryCommand<TCommand> command;
+
+        private List<TCommand> resultSet;
+        private int position = -1;
+
+        public ClientQueryAllEnumerator(IClient client, QueryCommand<TCommand> command)
+        {
+            if (client == null)
+            {
+                throw new FieldAccessException("Client cannot be null");
+            }
+
+            this.client = client;
+            this.command = command;
+        }
+
+        public bool MoveNext()
+        {
+            this.position++;
+            if (this.command.QueryParameters is IPageable pageableParameters && pageableParameters.Limit == null)
+            {
+                var limit = DefaultLimit;
+                if (this.resultSet == null || this.position % limit == 0)
+                {
+                    // Retrieve new set of results
+                    var originalOffset = pageableParameters.Offset;
+
+                    // Reset offset to original value after retrieveing for an eventual reuse of the command somewhere else
+                    pageableParameters.Offset = (originalOffset ?? DefaultOffset) + this.position;
+                    RetrieveResults(command);
+                    pageableParameters.Offset = originalOffset;
+                }
+
+                return this.position % limit < this.resultSet.Count;
+            }
+
+            // If command is not paged
+            if (this.resultSet == null)
+            {
+                RetrieveResults(this.command);
+            }
+
+            return this.position < this.resultSet.Count;
+        }
+
+        private void RetrieveResults(QueryCommand<TCommand> queryCommand)
+        {
+            var queryTask = this.client.ExecuteAsync(queryCommand);
+            this.resultSet = queryTask.Result.Results;
+        }
+
+        public void Reset()
+        {
+            this.resultSet = null;
+            this.position = -1;
+        }
+
+        public TCommand Current
+        {
+            get
+            {
+                if (this.command.QueryParameters is IPageable pageableParameters)
+                {
+                    var limit = pageableParameters.Limit ?? DefaultLimit;
+                    return this.resultSet[this.position % limit];
+                }
+
+                return this.resultSet[this.position];
+            }
+        }
+
+        object IEnumerator.Current => Current;
+
+        public void Dispose()
+        {
+            this.resultSet?.Clear();
+
+            this.client = null;
+            this.command = null;
+            this.resultSet = null;
+        }
+    }
+}
diff --git a/commercetools.Sdk/commercetools.Sdk.Client/ClientQueryProvider.cs b/commercetools.Sdk/commercetools.Sdk.Client/ClientQueryProvider.cs
index 9d3785e3..a935d683 100644
--- a/commercetools.Sdk/commercetools.Sdk.Client/ClientQueryProvider.cs
+++ b/commercetools.Sdk/commercetools.Sdk.Client/ClientQueryProvider.cs
@@ -15,11 +15,17 @@ public class ClientQueryProvider<T> : IQueryProvider
         private readonly IClient client;
 
         private IList<T> result = new List<T>();
+        private IEnumerator<T> enumerator;
 
-        public ClientQueryProvider(IClient client, QueryCommand<T> command)
+        public ClientQueryProvider(IClient client, QueryCommand<T> command, bool queryAll)
         {
             this.client = client;
             this.Command = command;
+
+            if (queryAll)
+            {
+                enumerator = new ClientQueryAllEnumerator<T>(client, command);
+            }
         }
 
         public QueryCommand<T> Command { get; }
@@ -128,6 +134,12 @@ public object Execute(Expression expression)
 
         public TResult Execute<TResult>(Expression expression)
         {
+            if (enumerator != null)
+            {
+                enumerator.Reset();
+                return (TResult)enumerator;
+            }
+
             if (this.client == null)
             {
                 throw new FieldAccessException("Client cannot be null");
diff --git a/commercetools.Sdk/commercetools.Sdk.Client/ClientQueryableCollection.cs b/commercetools.Sdk/commercetools.Sdk.Client/ClientQueryableCollection.cs
index 5d48b72d..381e53aa 100644
--- a/commercetools.Sdk/commercetools.Sdk.Client/ClientQueryableCollection.cs
+++ b/commercetools.Sdk/commercetools.Sdk.Client/ClientQueryableCollection.cs
@@ -11,9 +11,9 @@ namespace commercetools.Sdk.Client
 
     public class ClientQueryableCollection<T> : IOrderedQueryable<T>
     {
-        public ClientQueryableCollection(IClient client, QueryCommand<T> command)
+        public ClientQueryableCollection(IClient client, QueryCommand<T> command, bool queryAll = false)
         {
-            this.Provider = new ClientQueryProvider<T>(client, command);
+            this.Provider = new ClientQueryProvider<T>(client, command, queryAll);
             this.Expression = Expression.Constant(this);
         }
 
diff --git a/commercetools.Sdk/commercetools.Sdk.Client/LinqExtensions.cs b/commercetools.Sdk/commercetools.Sdk.Client/LinqExtensions.cs
index f2de9e1b..20ca6bef 100644
--- a/commercetools.Sdk/commercetools.Sdk.Client/LinqExtensions.cs
+++ b/commercetools.Sdk/commercetools.Sdk.Client/LinqExtensions.cs
@@ -16,6 +16,11 @@ public static ClientQueryableCollection<T> Query<T>(this IClient client)
             return new ClientQueryableCollection<T>(client, new QueryCommand<T>());
         }
 
+        public static ClientQueryableCollection<T> QueryAll<T>(this IClient client)
+        {
+            return new ClientQueryableCollection<T>(client, new QueryCommand<T>(), true);
+        }
+
         public static ClientQueryableCollection<ProductProjection> SearchProducts(this IClient client)
         {
             return new ClientQueryableCollection<ProductProjection>(client, new SearchProductProjectionsCommand());