private static IEnumerable <ActionResult> Flatten(CollectionActionResult collectionResult)
        {
            yield return(collectionResult);

            if (collectionResult.DocumentActionResults != null)
            {
                foreach (var result in collectionResult.DocumentActionResults)
                {
                    yield return(result);
                }
            }

            if (collectionResult.CollectionActionResults != null)
            {
                foreach (var result in collectionResult.CollectionActionResults)
                {
                    yield return(result);
                }
            }
        }
        private async Task <CollectionActionResult> ExecuteAsync(
            Uri sourceUrl,
            ICollectionNode sourceNode,
            TCollection target,
            IReadOnlyCollection <IUntypedWriteableProperty> properties,
            CancellationToken cancellationToken)
        {
            if (_logger.IsEnabled(LogLevel.Trace))
            {
                _logger.LogTrace($"Perform operation on collection {sourceUrl} with existing target {target.DestinationUrl}.");
            }

            var documentActionResults   = ImmutableList <ActionResult> .Empty;
            var collectionActionResults = ImmutableList <CollectionActionResult> .Empty;

            var subNodeProperties = new Dictionary <string, IReadOnlyCollection <IUntypedWriteableProperty> >();

            foreach (var childNode in sourceNode.Nodes)
            {
                var subProperties = await GetWriteableProperties(childNode.Collection, cancellationToken);

                subNodeProperties.Add(childNode.Name, subProperties);
            }

            foreach (var document in sourceNode.Documents)
            {
                var docUrl = sourceUrl.Append(document);
                if (target.Created)
                {
                    // Collection was created by us - we just assume that the document doesn't exist
                    var missingTarget = target.NewMissing(document.Name);
                    var docResult     = await ExecuteAsync(docUrl, document, missingTarget, cancellationToken);

                    documentActionResults = documentActionResults.Add(docResult);
                }
                else
                {
                    var foundTarget = await target.GetAsync(document.Name, cancellationToken);

                    var docTarget = foundTarget as TDocument;
                    if (docTarget != null)
                    {
                        // We found a document: Business as usual when we're allowed to overwrite it
                        var docResult = await ExecuteAsync(docUrl, document, docTarget, cancellationToken);

                        documentActionResults = documentActionResults.Add(docResult);
                    }
                    else
                    {
                        var collTarget = foundTarget as TCollection;
                        if (collTarget != null)
                        {
                            // We found a collection instead of a document
                            _logger.LogDebug($"{target.DestinationUrl}: Found a collection instead of a document");
                            var docResult = new ActionResult(ActionStatus.OverwriteFailed, foundTarget);
                            documentActionResults = documentActionResults.Add(docResult);
                        }
                        else
                        {
                            // We didn't find anything: Business as usual
                            var missingTarget = (TMissing)foundTarget;
                            var docResult     = await ExecuteAsync(docUrl, document, missingTarget, cancellationToken);

                            documentActionResults = documentActionResults.Add(docResult);
                        }
                    }
                }
            }

            foreach (var childNode in sourceNode.Nodes)
            {
                var childProperties = subNodeProperties[childNode.Name];
                var collection      = childNode.Collection;
                var docUrl          = sourceUrl.Append(childNode.Collection);
                if (target.Created)
                {
                    // Collection was created by us - we just assume that the sub collection doesn't exist
                    var missingTarget = target.NewMissing(childNode.Name);
                    var newColl       = await missingTarget.CreateCollectionAsync(cancellationToken);

                    var collResult = await ExecuteAsync(docUrl, childNode, newColl, childProperties, cancellationToken);

                    collectionActionResults = collectionActionResults.Add(collResult);
                }
                else
                {
                    // Test if the target node exists
                    var foundTarget = await target.GetAsync(collection.Name, cancellationToken);

                    var docTarget = foundTarget as TDocument;
                    if (docTarget != null)
                    {
                        // We found a document instead of a collection
                        _logger.LogDebug($"{target.DestinationUrl}: Found a document instead of a collection");
                        var collResult = new CollectionActionResult(ActionStatus.OverwriteFailed, foundTarget);
                        collectionActionResults = collectionActionResults.Add(collResult);
                    }
                    else
                    {
                        var collTarget = foundTarget as TCollection;
                        if (collTarget != null)
                        {
                            // We found a collection: Business as usual
                            var collResult = await ExecuteAsync(docUrl, childNode, collTarget, childProperties, cancellationToken);

                            collectionActionResults = collectionActionResults.Add(collResult);
                        }
                        else
                        {
                            // We didn't find anything: Business as usual
                            var missingTarget = (TMissing)foundTarget;
                            var newColl       = await missingTarget.CreateCollectionAsync(cancellationToken);

                            var collResult = await ExecuteAsync(docUrl, childNode, newColl, childProperties, cancellationToken);

                            collectionActionResults = collectionActionResults.Add(collResult);
                        }
                    }
                }
            }

            try
            {
                if (_logger.IsEnabled(LogLevel.Trace))
                {
                    _logger.LogTrace($"Set properties on collection {target.DestinationUrl}.");
                }

                var failedPropertyNames = await target.SetPropertiesAsync(properties, cancellationToken);

                if (failedPropertyNames.Count != 0)
                {
                    _logger.LogDebug($"{target.DestinationUrl}: Failed setting properties {string.Join(", ", failedPropertyNames.Select(x => x.ToString()))}");
                    return(new CollectionActionResult(ActionStatus.PropSetFailed, target)
                    {
                        FailedProperties = failedPropertyNames,
                        CollectionActionResults = collectionActionResults,
                        DocumentActionResults = documentActionResults,
                    });
                }

                await _handler.ExecuteAsync(sourceNode.Collection, target, cancellationToken);
            }
            catch (Exception ex)
            {
                _logger.LogDebug($"{sourceNode.Collection.Path}: Cleanup failed with exception {ex.Message}");
                return(new CollectionActionResult(ActionStatus.CleanupFailed, target)
                {
                    Exception = ex,
                    CollectionActionResults = collectionActionResults,
                    DocumentActionResults = documentActionResults,
                });
            }

            return(new CollectionActionResult(ActionStatus.Created, target)
            {
                CollectionActionResults = collectionActionResults,
                DocumentActionResults = documentActionResults,
            });
        }