/// <inheritdoc/>
        public virtual Task <ListTenantsApiCall> PrepareListTenantsAsync(CancellationToken cancellationToken)
        {
            UriTemplate template = new UriTemplate("v2.0/tenants");
            Dictionary <string, string> parameters = new Dictionary <string, string>();

            Func <HttpResponseMessage, CancellationToken, Task <ReadOnlyCollectionPage <Tenant> > > deserializeResult =
                (responseMessage, innerCancellationToken) =>
            {
                Uri originalUri = responseMessage.RequestMessage.RequestUri;

                if (!HttpApiCall.IsAcceptable(responseMessage))
                {
                    throw new HttpWebException(responseMessage);
                }

                return(responseMessage.Content.ReadAsStringAsync()
                       .Select(
                           innerTask =>
                {
                    if (string.IsNullOrEmpty(innerTask.Result))
                    {
                        return null;
                    }

                    JObject responseObject = JsonConvert.DeserializeObject <JObject>(innerTask.Result);
                    JArray tenantsArray = responseObject["tenants"] as JArray;
                    if (tenantsArray == null)
                    {
                        return null;
                    }

                    IList <Tenant> list = tenantsArray.ToObject <Tenant[]>();
                    // http://docs.openstack.org/api/openstack-identity-service/2.0/content/Paginated_Collections-d1e325.html
                    Func <CancellationToken, Task <IHttpApiCall <ReadOnlyCollectionPage <Tenant> > > > prepareGetNextPageAsync = null;
                    JArray tenantsLinksArray = responseObject["tenants_links"] as JArray;
                    if (tenantsLinksArray != null)
                    {
                        IList <Link> tenantsLinks = tenantsLinksArray.ToObject <Link[]>();
                        Link nextLink = tenantsLinks.FirstOrDefault(i => string.Equals("next", i.Relation, StringComparison.OrdinalIgnoreCase));
                        if (nextLink != null)
                        {
                            prepareGetNextPageAsync =
                                nextCancellationToken =>
                            {
                                return PrepareListTenantsAsync(nextCancellationToken)
                                .WithUri(nextLink.Target)
                                .Select(_ => _.Result.AsHttpApiCall());
                            };
                        }
                    }

                    ReadOnlyCollectionPage <Tenant> results = new BasicReadOnlyCollectionPage <Tenant>(list, prepareGetNextPageAsync);
                    return results;
                }));
            };

            return(GetBaseUriAsync(cancellationToken)
                   .Then(PrepareRequestAsyncFunc(HttpMethod.Get, template, parameters, cancellationToken))
                   .Select(task => new ListTenantsApiCall(CreateCustomApiCall(task.Result, HttpCompletionOption.ResponseContentRead, deserializeResult))));
        }
        /// <inheritdoc/>
        public virtual Task <ListApiVersionsApiCall> PrepareListApiVersionsAsync(CancellationToken cancellationToken)
        {
            UriTemplate template = new UriTemplate(string.Empty);
            IDictionary <string, string> parameters = ImmutableDictionary <string, string> .Empty;

            Func <HttpResponseMessage, CancellationToken, Task <ReadOnlyCollectionPage <ApiVersion> > > deserializeResult =
                (responseMessage, innerCancellationToken) =>
            {
                Uri originalUri = responseMessage.RequestMessage.RequestUri;

                if (!HttpApiCall.IsAcceptable(responseMessage))
                {
                    throw new HttpWebException(responseMessage);
                }

                return(responseMessage.Content.ReadAsStringAsync()
                       .Select(
                           innerTask =>
                {
                    if (string.IsNullOrEmpty(innerTask.Result))
                    {
                        return null;
                    }

                    JObject responseObject = JsonConvert.DeserializeObject <JObject>(innerTask.Result);
                    JObject versionsObject = responseObject["versions"] as JObject;
                    if (versionsObject == null)
                    {
                        return null;
                    }

                    JArray versionsArray = versionsObject["values"] as JArray;
                    if (versionsArray == null)
                    {
                        return null;
                    }

                    IList <ApiVersion> list = versionsArray.ToObject <ApiVersion[]>();
                    // according to the available documentation, this call does not appear to be paginated
                    Func <CancellationToken, Task <IHttpApiCall <ReadOnlyCollectionPage <ApiVersion> > > > prepareGetNextPageAsync = null;

                    ReadOnlyCollectionPage <ApiVersion> results = new BasicReadOnlyCollectionPage <ApiVersion>(list, prepareGetNextPageAsync);
                    return results;
                }));
            };

            return(GetBaseUriAsync(cancellationToken)
                   .Then(PrepareRequestAsyncFunc(HttpMethod.Get, template, parameters, cancellationToken))
                   .Select(task => new ListApiVersionsApiCall(CreateCustomApiCall(task.Result, HttpCompletionOption.ResponseContentRead, deserializeResult))));
        }
        /// <inheritdoc/>
        public Task <ReadOnlyCollectionPage <CloudQueue> > ListQueuesAsync(QueueName marker, int?limit, bool detailed, CancellationToken cancellationToken)
        {
            if (limit <= 0)
            {
                throw new ArgumentOutOfRangeException("limit");
            }

            UriTemplate template   = new UriTemplate("/queues?marker={marker}&limit={limit}&detailed={detailed}");
            var         parameters = new Dictionary <string, string>
            {
                { "detailed", detailed.ToString().ToLowerInvariant() },
            };

            if (marker != null)
            {
                parameters.Add("marker", marker.Value);
            }
            if (limit.HasValue)
            {
                parameters.Add("limit", limit.ToString());
            }

            Func <Task <Tuple <IdentityToken, Uri> >, HttpWebRequest> prepareRequest =
                PrepareRequestAsyncFunc(HttpMethod.GET, template, parameters);

            Func <Task <HttpWebRequest>, Task <ListCloudQueuesResponse> > requestResource =
                GetResponseAsyncFunc <ListCloudQueuesResponse>(cancellationToken);

            Func <Task <ListCloudQueuesResponse>, ReadOnlyCollectionPage <CloudQueue> > resultSelector =
                task =>
            {
                ReadOnlyCollectionPage <CloudQueue> page = null;
                if (task.Result != null && task.Result.Queues != null)
                {
                    CloudQueue lastQueue  = task.Result.Queues.LastOrDefault();
                    QueueName  nextMarker = lastQueue != null ? lastQueue.Name : marker;
                    Func <CancellationToken, Task <ReadOnlyCollectionPage <CloudQueue> > > getNextPageAsync =
                        nextCancellationToken => ListQueuesAsync(nextMarker, limit, detailed, nextCancellationToken);
                    page = new BasicReadOnlyCollectionPage <CloudQueue>(task.Result.Queues, getNextPageAsync);
                }

                return(page ?? ReadOnlyCollectionPage <CloudQueue> .Empty);
            };

            return(AuthenticateServiceAsync(cancellationToken)
                   .Select(prepareRequest)
                   .Then(requestResource)
                   .Select(resultSelector));
        }
        /// <inheritdoc/>
        public override Task<ListExtensionsApiCall> PrepareListExtensionsAsync(CancellationToken cancellationToken)
        {
            UriTemplate template = new UriTemplate("v2.0/extensions");
            Dictionary<string, string> parameters = new Dictionary<string, string>();

            Func<HttpResponseMessage, CancellationToken, Task<ReadOnlyCollectionPage<Extension>>> deserializeResult =
                (responseMessage, innerCancellationToken) =>
                {
                    Uri originalUri = responseMessage.RequestMessage.RequestUri;

                    if (!HttpApiCall.IsAcceptable(responseMessage))
                        throw new HttpWebException(responseMessage);

                    return responseMessage.Content.ReadAsStringAsync()
                        .Select(
                            innerTask =>
                            {
                                if (string.IsNullOrEmpty(innerTask.Result))
                                    return null;

                                JObject responseObject = JsonConvert.DeserializeObject<JObject>(innerTask.Result);

                                // Unlike OpenStack, Rackspace does not wrap the array into a child object 'extensions.values'
                                JArray extensionsArray = responseObject["extensions"] as JArray;
                                if (extensionsArray == null)
                                    return null;

                                IList<Extension> list = extensionsArray.ToObject<Extension[]>();
                                // http://docs.openstack.org/api/openstack-identity-service/2.0/content/Paginated_Collections-d1e325.html
                                Func<CancellationToken, Task<IHttpApiCall<ReadOnlyCollectionPage<Extension>>>> prepareGetNextPageAsync = null;
                                JArray extensionsLinksArray = responseObject["extensions_links"] as JArray;
                                if (extensionsLinksArray != null)
                                {
                                    IList<Link> extensionsLinks = extensionsLinksArray.ToObject<Link[]>();
                                    Link nextLink = extensionsLinks.FirstOrDefault(i => string.Equals("next", i.Relation, StringComparison.OrdinalIgnoreCase));
                                    if (nextLink != null)
                                    {
                                        prepareGetNextPageAsync =
                                            nextCancellationToken =>
                                            {
                                                return PrepareListExtensionsAsync(nextCancellationToken)
                                                    .WithUri(nextLink.Target)
                                                    .Select(_ => _.Result.AsHttpApiCall());
                                                ;
                                            };
                                    }
                                }

                                ReadOnlyCollectionPage<Extension> results = new BasicReadOnlyCollectionPage<Extension>(list, prepareGetNextPageAsync);
                                return results;
                            });
                };

            return GetBaseUriAsync(cancellationToken)
                .Then(PrepareRequestAsyncFunc(HttpMethod.Get, template, parameters, cancellationToken))
                .Select(task => new ListExtensionsApiCall(CreateCustomApiCall(task.Result, HttpCompletionOption.ResponseContentRead, deserializeResult)));
        }