< Summary

Class:Microsoft.Azure.ServiceBus.Management.ManagementClient
Assembly:Microsoft.Azure.ServiceBus
File(s):C:\Git\azure-sdk-for-net\sdk\servicebus\Microsoft.Azure.ServiceBus\src\Management\ManagementClient.cs
Covered lines:15
Uncovered lines:337
Coverable lines:352
Total lines:1234
Line coverage:4.2% (15 of 352)
Covered branches:8
Total branches:128
Branch coverage:6.2% (8 of 128)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.ctor(...)-0%100%
.ctor(...)-0%100%
.ctor(...)-100%100%
CloneRequest(...)-0%0%
GetNamespaceInfoAsync()-0%100%
DeleteQueueAsync(...)-0%100%
DeleteTopicAsync(...)-0%100%
DeleteSubscriptionAsync(...)-0%100%
DeleteRuleAsync(...)-0%100%
GetQueueAsync()-0%100%
GetTopicAsync()-0%100%
GetSubscriptionAsync()-0%100%
GetRuleAsync()-0%100%
GetQueueRuntimeInfoAsync()-0%100%
GetTopicRuntimeInfoAsync()-0%100%
GetSubscriptionRuntimeInfoAsync()-0%100%
GetQueuesAsync()-0%0%
GetTopicsAsync()-0%0%
GetSubscriptionsAsync()-0%0%
GetRulesAsync()-0%0%
GetQueuesRuntimeInfoAsync()-0%0%
GetTopicsRuntimeInfoAsync()-0%0%
GetSubscriptionsRuntimeInfoAsync()-0%0%
CreateQueueAsync(...)-0%100%
CreateQueueAsync()-0%0%
CreateTopicAsync(...)-0%100%
CreateTopicAsync()-0%0%
CreateSubscriptionAsync(...)-0%100%
CreateSubscriptionAsync(...)-0%0%
CreateSubscriptionAsync()-0%0%
CreateRuleAsync()-0%0%
UpdateQueueAsync()-0%0%
UpdateTopicAsync()-0%0%
UpdateSubscriptionAsync()-0%0%
UpdateRuleAsync()-0%0%
QueueExistsAsync()-0%100%
TopicExistsAsync()-0%100%
SubscriptionExistsAsync()-0%100%
CloseAsync()-0%0%
GetPort(...)-66.67%50%
ValidateHttpResponse()-0%0%
ParseDetailIfAvailable(...)-0%0%
CreateTokenProvider(...)-71.43%62.5%
GetToken(...)-0%100%
GetToken()-0%100%
GetEntity()-0%0%
PutEntity()-0%0%
DeleteEntity()-0%100%
SendHttpRequest()-0%0%

File(s)

C:\Git\azure-sdk-for-net\sdk\servicebus\Microsoft.Azure.ServiceBus\src\Management\ManagementClient.cs

#LineLine coverage
 1// Copyright (c) Microsoft. All rights reserved.
 2// Licensed under the MIT license. See LICENSE file in the project root for full license information.
 3
 4namespace Microsoft.Azure.ServiceBus.Management
 5{
 6    using System;
 7    using System.Collections.Generic;
 8    using System.Net;
 9    using System.Net.Http;
 10    using System.Text;
 11    using System.Threading;
 12    using System.Threading.Tasks;
 13    using System.Xml.Linq;
 14    using Microsoft.Azure.ServiceBus.Primitives;
 15
 16    public class ManagementClient
 17    {
 18        private HttpClient httpClient;
 19        private readonly string endpointFQDN;
 20        private readonly ITokenProvider tokenProvider;
 21        private readonly int port;
 22        private readonly string clientId;
 23
 24        /// <summary>
 25        /// Initializes a new <see cref="ManagementClient"/> which can be used to perform management opertions on Servic
 26        /// </summary>
 27        /// <param name="connectionString">Namespace connection string.</param>
 28        public ManagementClient(string connectionString)
 029            : this(new ServiceBusConnectionStringBuilder(connectionString))
 30        {
 031        }
 32
 33        /// <summary>
 34        /// Initializes a new <see cref="ManagementClient"/> which can be used to perform management opertions on Servic
 35        /// </summary>
 36        /// <param name="endpoint">Fully qualified domain name for Service Bus. Most likely, {yournamespace}.servicebus.
 37        /// <param name="tokenProvider">Token provider which will generate security tokens for authorization.</param>
 38        public ManagementClient(string endpoint, ITokenProvider tokenProvider)
 039            : this(new ServiceBusConnectionStringBuilder {Endpoint = endpoint}, tokenProvider)
 40        {
 041        }
 42
 43        /// <summary>
 44        /// Initializes a new <see cref="ManagementClient"/> which can be used to perform management opertions on Servic
 45        /// </summary>
 46        /// <param name="connectionStringBuilder"><see cref="ServiceBusConnectionStringBuilder"/> having endpoint inform
 47        /// <param name="tokenProvider">Token provider which will generate security tokens for authorization.</param>
 1248        public ManagementClient(ServiceBusConnectionStringBuilder connectionStringBuilder, ITokenProvider tokenProvider 
 49        {
 1250            this.httpClient = new HttpClient { Timeout = connectionStringBuilder.OperationTimeout };
 1251            this.endpointFQDN = connectionStringBuilder.Endpoint;
 1252            this.tokenProvider = tokenProvider ?? CreateTokenProvider(connectionStringBuilder);
 653            this.port = GetPort(connectionStringBuilder.Endpoint);
 654            this.clientId = nameof(ManagementClient) + Guid.NewGuid().ToString("N").Substring(0, 6);
 55
 656            MessagingEventSource.Log.ManagementClientCreated(this.clientId, this.httpClient.Timeout.TotalSeconds, this.t
 657        }
 58
 59        public static HttpRequestMessage CloneRequest(HttpRequestMessage req)
 60        {
 061            HttpRequestMessage clone = new HttpRequestMessage(req.Method, req.RequestUri);
 62
 063            clone.Content = req.Content;
 064            clone.Version = req.Version;
 65
 066            foreach (KeyValuePair<string, object> prop in req.Properties)
 67            {
 068                clone.Properties.Add(prop);
 69            }
 70
 071            foreach (KeyValuePair<string, IEnumerable<string>> header in req.Headers)
 72            {
 073                clone.Headers.TryAddWithoutValidation(header.Key, header.Value);
 74            }
 75
 076            return clone;
 77        }
 78
 79        /// <summary>
 80        /// Gets information related to the currently used namespace.
 81        /// </summary>
 82        /// <param name="cancellationToken"></param>
 83        /// <returns><see cref="NamespaceInfo"/> containing namespace information.</returns>
 84        /// <remarks>Works with any claim (Send/Listen/Manage).</remarks>
 85        public virtual async Task<NamespaceInfo> GetNamespaceInfoAsync(CancellationToken cancellationToken = default)
 86        {
 087            var content = await GetEntity("$namespaceinfo", null, false, cancellationToken).ConfigureAwait(false);
 088            return NamespaceInfoExtensions.ParseFromContent(content);
 089        }
 90
 91        #region DeleteEntity
 92
 93        /// <summary>
 94        /// Deletes the queue described by the path relative to the service namespace base address.
 95        /// </summary>
 96        /// <param name="queuePath">The name of the queue relative to the service namespace base address.</param>
 97        /// <param name="cancellationToken"></param>
 98        /// <exception cref="ArgumentException"><paramref name="queuePath"/> is empty or null, or path starts or ends wi
 99        /// <exception cref="ArgumentOutOfRangeException">The length of path is greater than 260.</exception>
 100        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 101        /// <exception cref="MessagingEntityNotFoundException">Queue with this name does not exist.</exception>
 102        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 103        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 104        /// <exception cref="ServiceBusException">An internal error or an unexpected exception occured.</exception>
 105        public virtual Task DeleteQueueAsync(string queuePath, CancellationToken cancellationToken = default)
 106        {
 0107            EntityNameHelper.CheckValidQueueName(queuePath);
 0108            return DeleteEntity(queuePath, cancellationToken);
 109        }
 110
 111        /// <summary>
 112        /// Deletes the topic described by the name relative to the service namespace base address.
 113        /// </summary>
 114        /// <param name="topicPath">The name of the topic relative to the service namespace base address.</param>
 115        /// <param name="cancellationToken"></param>
 116        /// <exception cref="ArgumentException"><paramref name="topicPath"/> is empty or null, or path starts or ends wi
 117        /// <exception cref="ArgumentOutOfRangeException">The length of topic path is greater than 260.</exception>
 118        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 119        /// <exception cref="MessagingEntityNotFoundException">Topic with this name does not exist.</exception>
 120        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 121        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 122        /// <exception cref="ServiceBusException">An internal error or an unexpected exception occured.</exception>
 123        public virtual Task DeleteTopicAsync(string topicPath, CancellationToken cancellationToken = default)
 124        {
 0125            EntityNameHelper.CheckValidTopicName(topicPath);
 0126            return DeleteEntity(topicPath, cancellationToken);
 127        }
 128
 129        /// <summary>
 130        /// Deletes the subscription with the specified topic and subscription name.
 131        /// </summary>
 132        /// <param name="topicPath">The name of the topic relative to the service namespace base address.</param>
 133        /// <param name="subscriptionName">The name of the subscription to delete.</param>
 134        /// <param name="cancellationToken"></param>
 135        /// <exception cref="ArgumentException"><paramref name="topicPath"/> or <paramref name="subscriptionName"/> is e
 136        /// <exception cref="ArgumentOutOfRangeException">The length of topic path is greater than 260 or length of subs
 137        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 138        /// <exception cref="MessagingEntityNotFoundException">Subscription with this name does not exist.</exception>
 139        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 140        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 141        /// <exception cref="ServiceBusException">An internal error or an unexpected exception occured.</exception>
 142        public virtual Task DeleteSubscriptionAsync(string topicPath, string subscriptionName, CancellationToken cancell
 143        {
 0144            EntityNameHelper.CheckValidTopicName(topicPath);
 0145            EntityNameHelper.CheckValidSubscriptionName(subscriptionName);
 146
 0147            return DeleteEntity(EntityNameHelper.FormatSubscriptionPath(topicPath, subscriptionName), cancellationToken)
 148        }
 149
 150        /// <summary>
 151        /// Deletes the rule described by <paramref name="ruleName"/> from <paramref name="subscriptionName"/> under <pa
 152        /// </summary>
 153        /// <param name="topicPath">The name of the topic relative to the service namespace base address.</param>
 154        /// <param name="subscriptionName">The name of the subscription to delete.</param>
 155        /// <param name="ruleName">The name of the rule to delete.</param>
 156        /// <param name="cancellationToken"></param>
 157        /// <exception cref="ArgumentException">Thrown if <paramref name="topicPath"/>, <paramref name="subscriptionName
 158        /// <exception cref="ArgumentOutOfRangeException">The length of topic path is greater than 260 or length of subs
 159        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 160        /// <exception cref="MessagingEntityNotFoundException">Rule with this name does not exist.</exception>
 161        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 162        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 163        /// <exception cref="ServiceBusException">An internal error or an unexpected exception occured.</exception>
 164        public virtual Task DeleteRuleAsync(string topicPath, string subscriptionName, string ruleName, CancellationToke
 165        {
 0166            EntityNameHelper.CheckValidTopicName(topicPath);
 0167            EntityNameHelper.CheckValidSubscriptionName(subscriptionName);
 0168            EntityNameHelper.CheckValidRuleName(ruleName);
 169
 0170            return DeleteEntity(EntityNameHelper.FormatRulePath(topicPath, subscriptionName, ruleName), cancellationToke
 171        }
 172
 173        #endregion
 174
 175        #region GetEntity
 176
 177        /// <summary>
 178        /// Retrieves a queue from the service namespace.
 179        /// </summary>
 180        /// <param name="queuePath">The path of the queue relative to service bus namespace.</param>
 181        /// <param name="cancellationToken"></param>
 182        /// <returns><see cref="QueueDescription"/> containing information about the queue.</returns>
 183        /// <exception cref="ArgumentException">Thrown if <paramref name="queuePath"/> is null, white space empty or not
 184        /// <exception cref="ArgumentOutOfRangeException">The length of queue path is greater than 260.</exception>
 185        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 186        /// <exception cref="MessagingEntityNotFoundException">Queue with this name does not exist.</exception>
 187        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 188        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 189        /// <exception cref="ServiceBusException">An internal error or an unexpected exception occured.</exception>
 190        public virtual async Task<QueueDescription> GetQueueAsync(string queuePath, CancellationToken cancellationToken 
 191        {
 0192            EntityNameHelper.CheckValidQueueName(queuePath);
 193
 0194            var content = await GetEntity(queuePath, null, false, cancellationToken).ConfigureAwait(false);
 195
 0196            return QueueDescriptionExtensions.ParseFromContent(content);
 0197        }
 198
 199        /// <summary>
 200        /// Retrieves a topic from the service namespace.
 201        /// </summary>
 202        /// <param name="topicPath">The path of the topic relative to service bus namespace.</param>
 203        /// <param name="cancellationToken"></param>
 204        /// <returns><see cref="TopicDescription"/> containing information about the topic.</returns>
 205        /// <exception cref="ArgumentException">Thrown if <paramref name="topicPath"/> is null, white space empty or not
 206        /// <exception cref="ArgumentOutOfRangeException">The length of topic path is greater than 260.</exception>
 207        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 208        /// <exception cref="MessagingEntityNotFoundException">Topic with this name does not exist.</exception>
 209        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 210        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 211        /// <exception cref="ServiceBusException">An internal error or an unexpected exception occured.</exception>
 212        public virtual async Task<TopicDescription> GetTopicAsync(string topicPath, CancellationToken cancellationToken 
 213        {
 0214            EntityNameHelper.CheckValidTopicName(topicPath);
 215
 0216            var content = await GetEntity(topicPath, null, false, cancellationToken).ConfigureAwait(false);
 217
 0218            return TopicDescriptionExtensions.ParseFromContent(content);
 0219        }
 220
 221        /// <summary>
 222        /// Retrieves a subscription from the service namespace.
 223        /// </summary>
 224        /// <param name="topicPath">The path of the topic relative to service bus namespace.</param>
 225        /// <param name="subscriptionName">The subscription name.</param>
 226        /// <param name="cancellationToken"></param>
 227        /// <returns><see cref="SubscriptionDescription"/> containing information about the subscription.</returns>
 228        /// <exception cref="ArgumentException">Thrown if <paramref name="topicPath"/>, <paramref name="subscriptionName
 229        /// <exception cref="ArgumentOutOfRangeException">The length of topic path is greater than 260 or length of subs
 230        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 231        /// <exception cref="MessagingEntityNotFoundException">Topic or Subscription with this name does not exist.</exc
 232        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 233        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 234        /// <exception cref="ServiceBusException">An internal error or an unexpected exception occured.</exception>
 235        public virtual async Task<SubscriptionDescription> GetSubscriptionAsync(string topicPath, string subscriptionNam
 236        {
 0237            EntityNameHelper.CheckValidTopicName(topicPath);
 0238            EntityNameHelper.CheckValidSubscriptionName(subscriptionName);
 239
 0240            var content = await GetEntity(EntityNameHelper.FormatSubscriptionPath(topicPath, subscriptionName), null, fa
 241
 0242            return SubscriptionDescriptionExtensions.ParseFromContent(topicPath, content);
 0243        }
 244
 245        /// <summary>
 246        /// Retrieves a rule from the service namespace.
 247        /// </summary>
 248        /// <param name="topicPath">The path of the topic relative to service bus namespace.</param>
 249        /// <param name="subscriptionName">The subscription name the rule belongs to.</param>
 250        /// <param name="ruleName">The name of the rule to retrieve.</param>
 251        /// <param name="cancellationToken"></param>
 252        /// <returns><see cref="RuleDescription"/> containing information about the rule.</returns>
 253        /// <exception cref="ArgumentException">Thrown if <paramref name="topicPath"/>, <paramref name="subscriptionName
 254        /// <exception cref="ArgumentOutOfRangeException">The length of topic path is greater than 260 or length of subs
 255        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 256        /// <exception cref="MessagingEntityNotFoundException">Topic/Subscription/Rule with this name does not exist.</e
 257        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 258        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 259        /// <exception cref="ServiceBusException">An internal error or an unexpected exception occured.</exception>
 260        /// <remarks>Note - Only following data types are deserialized in Filters and Action parameters - string,int,lon
 261        /// Other data types would return its string value.</remarks>
 262        public virtual async Task<RuleDescription> GetRuleAsync(string topicPath, string subscriptionName, string ruleNa
 263        {
 0264            EntityNameHelper.CheckValidTopicName(topicPath);
 0265            EntityNameHelper.CheckValidSubscriptionName(subscriptionName);
 0266            EntityNameHelper.CheckValidRuleName(ruleName);
 267
 0268            var content = await GetEntity(EntityNameHelper.FormatRulePath(topicPath, subscriptionName, ruleName), null, 
 269
 0270            return RuleDescriptionExtensions.ParseFromContent(content);
 0271        }
 272
 273        #endregion
 274
 275        #region GetRuntimeInfo
 276        /// <summary>
 277        /// Retrieves the runtime information of a queue.
 278        /// </summary>
 279        /// <param name="queuePath">The path of the queue relative to service bus namespace.</param>
 280        /// <param name="cancellationToken"></param>
 281        /// <returns><see cref="QueueRuntimeInfo"/> containing runtime information about the queue.</returns>
 282        /// <exception cref="ArgumentException">Thrown if <paramref name="queuePath"/> is null, white space empty or not
 283        /// <exception cref="ArgumentOutOfRangeException">The length of queue path is greater than 260.</exception>
 284        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 285        /// <exception cref="MessagingEntityNotFoundException">Queue with this name does not exist.</exception>
 286        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 287        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 288        /// <exception cref="ServiceBusException">An internal error or an unexpected exception occured.</exception>
 289        public virtual async Task<QueueRuntimeInfo> GetQueueRuntimeInfoAsync(string queuePath, CancellationToken cancell
 290        {
 0291            EntityNameHelper.CheckValidQueueName(queuePath);
 292
 0293            var content = await GetEntity(queuePath, null, true, cancellationToken).ConfigureAwait(false);
 294
 0295            return QueueRuntimeInfoExtensions.ParseFromContent(content);
 0296        }
 297
 298        /// <summary>
 299        /// Retrieves the runtime information of a topic.
 300        /// </summary>
 301        /// <param name="topicPath">The path of the topic relative to service bus namespace.</param>
 302        /// <param name="cancellationToken"></param>
 303        /// <returns><see cref="TopicRuntimeInfo"/> containing runtime information about the topic.</returns>
 304        /// <exception cref="ArgumentException">Thrown if <paramref name="topicPath"/> is null, white space empty or not
 305        /// <exception cref="ArgumentOutOfRangeException">The length of topic path is greater than 260.</exception>
 306        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 307        /// <exception cref="MessagingEntityNotFoundException">Topic with this name does not exist.</exception>
 308        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 309        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 310        /// <exception cref="ServiceBusException">An internal error or an unexpected exception occured.</exception>
 311        public virtual async Task<TopicRuntimeInfo> GetTopicRuntimeInfoAsync(string topicPath, CancellationToken cancell
 312        {
 0313            EntityNameHelper.CheckValidTopicName(topicPath);
 314
 0315            var content = await GetEntity(topicPath, null, true, cancellationToken).ConfigureAwait(false);
 316
 0317            return TopicRuntimeInfoExtensions.ParseFromContent(content);
 0318        }
 319
 320        /// <summary>
 321        /// Retrieves the runtime information of a subscription.
 322        /// </summary>
 323        /// <param name="topicPath">The path of the topic relative to service bus namespace.</param>
 324        /// <param name="subscriptionName">The subscription name.</param>
 325        /// <param name="cancellationToken"></param>
 326        /// <returns><see cref="SubscriptionRuntimeInfo"/> containing runtime information about the subscription.</retur
 327        /// <exception cref="ArgumentException">Thrown if <paramref name="topicPath"/>, <paramref name="subscriptionName
 328        /// <exception cref="ArgumentOutOfRangeException">The length of topic path is greater than 260 or length of subs
 329        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 330        /// <exception cref="MessagingEntityNotFoundException">Topic or Subscription with this name does not exist.</exc
 331        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 332        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 333        /// <exception cref="ServiceBusException">An internal error or an unexpected exception occured.</exception>
 334        public virtual async Task<SubscriptionRuntimeInfo> GetSubscriptionRuntimeInfoAsync(string topicPath, string subs
 335        {
 0336            EntityNameHelper.CheckValidTopicName(topicPath);
 0337            EntityNameHelper.CheckValidSubscriptionName(subscriptionName);
 338
 0339            var content = await GetEntity(EntityNameHelper.FormatSubscriptionPath(topicPath, subscriptionName), null, tr
 340
 0341            return SubscriptionRuntimeInfoExtensions.ParseFromContent(topicPath, content);
 0342        }
 343
 344        #endregion
 345
 346        #region GetEntities
 347        /// <summary>
 348        /// Retrieves the list of queues present in the namespace.
 349        /// </summary>
 350        /// <param name="count">The number of queues to fetch. Defaults to 100. Maximum value allowed is 100.</param>
 351        /// <param name="skip">The number of queues to skip. Defaults to 0. Cannot be negative.</param>
 352        /// <param name="cancellationToken"></param>
 353        /// <returns><see cref="IList&lt;QueueDescription&gt;"/> containing list of queues.</returns>
 354        /// <exception cref="ArgumentOutOfRangeException">If the parameters are out of range.</exception>
 355        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 356        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 357        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 358        /// <exception cref="ServiceBusException">An internal error or an unexpected exception occured.</exception>
 359        /// <remarks>You can simulate pages of list of entities by manipulating <paramref name="count"/> and <paramref n
 360        /// skip(0)+count(100) gives first 100 entities. skip(100)+count(100) gives the next 100 entities.</remarks>
 361        public virtual async Task<IList<QueueDescription>> GetQueuesAsync(int count = 100, int skip = 0, CancellationTok
 362        {
 0363            if (count > 100 || count < 1)
 364            {
 0365                throw new ArgumentOutOfRangeException(nameof(count), "Value should be between 1 and 100");
 366            }
 0367            if (skip < 0)
 368            {
 0369                throw new ArgumentOutOfRangeException(nameof(skip), "Value cannot be negative");
 370            }
 371
 0372            var content = await GetEntity("$Resources/queues", $"$skip={skip}&$top={count}", false, cancellationToken).C
 0373            return QueueDescriptionExtensions.ParseCollectionFromContent(content);
 0374        }
 375
 376        /// <summary>
 377        /// Retrieves the list of topics present in the namespace.
 378        /// </summary>
 379        /// <param name="count">The number of topics to fetch. Defaults to 100. Maximum value allowed is 100.</param>
 380        /// <param name="skip">The number of topics to skip. Defaults to 0. Cannot be negative.</param>
 381        /// <param name="cancellationToken"></param>
 382        /// <returns><see cref="IList&lt;TopicDescription&gt;"/> containing list of topics.</returns>
 383        /// <exception cref="ArgumentOutOfRangeException">If the parameters are out of range.</exception>
 384        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 385        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 386        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 387        /// <exception cref="ServiceBusException">An internal error or an unexpected exception occured.</exception>
 388        /// <remarks>You can simulate pages of list of entities by manipulating <paramref name="count"/> and <paramref n
 389        /// skip(0)+count(100) gives first 100 entities. skip(100)+count(100) gives the next 100 entities.</remarks>
 390        public virtual async Task<IList<TopicDescription>> GetTopicsAsync(int count = 100, int skip = 0, CancellationTok
 391        {
 0392            if (count > 100 || count < 1)
 393            {
 0394                throw new ArgumentOutOfRangeException(nameof(count), "Value should be between 1 and 100");
 395            }
 0396            if (skip < 0)
 397            {
 0398                throw new ArgumentOutOfRangeException(nameof(skip), "Value cannot be negative");
 399            }
 400
 0401            var content = await GetEntity("$Resources/topics", $"$skip={skip}&$top={count}", false, cancellationToken).C
 0402            return TopicDescriptionExtensions.ParseCollectionFromContent(content);
 0403        }
 404
 405        /// <summary>
 406        /// Retrieves the list of subscriptions present in the topic.
 407        /// </summary>
 408        /// <param name="topicPath">The topic path under which all the subscriptions need to be retrieved.</param>
 409        /// <param name="count">The number of subscriptions to fetch. Defaults to 100. Maximum value allowed is 100.</pa
 410        /// <param name="skip">The number of subscriptions to skip. Defaults to 0. Cannot be negative.</param>
 411        /// <param name="cancellationToken"></param>
 412        /// <returns><see cref="IList&lt;SubscriptionDescription&gt;"/> containing list of subscriptions.</returns>
 413        /// <exception cref="ArgumentOutOfRangeException">If the parameters are out of range.</exception>
 414        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 415        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 416        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 417        /// <exception cref="ServiceBusException">An internal error or an unexpected exception occured.</exception>
 418        /// <remarks>You can simulate pages of list of entities by manipulating <paramref name="count"/> and <paramref n
 419        /// skip(0)+count(100) gives first 100 entities. skip(100)+count(100) gives the next 100 entities.</remarks>
 420        public virtual async Task<IList<SubscriptionDescription>> GetSubscriptionsAsync(string topicPath, int count = 10
 421        {
 0422            EntityNameHelper.CheckValidTopicName(topicPath);
 0423            if (count > 100 || count < 1)
 424            {
 0425                throw new ArgumentOutOfRangeException(nameof(count), "Value should be between 1 and 100");
 426            }
 0427            if (skip < 0)
 428            {
 0429                throw new ArgumentOutOfRangeException(nameof(skip), "Value cannot be negative");
 430            }
 431
 0432            var content = await GetEntity($"{topicPath}/Subscriptions", $"$skip={skip}&$top={count}", false, cancellatio
 0433            return SubscriptionDescriptionExtensions.ParseCollectionFromContent(topicPath, content);
 0434        }
 435
 436        /// <summary>
 437        /// Retrieves the list of rules for a given subscription in a topic.
 438        /// </summary>
 439        /// <param name="topicPath">The topic path.</param>
 440        /// <param name="subscriptionName"> The subscription for which all the rules need to be retrieved.</param>
 441        /// <param name="count">The number of rules to fetch. Defaults to 100. Maximum value allowed is 100.</param>
 442        /// <param name="skip">The number of rules to skip. Defaults to 0. Cannot be negative.</param>
 443        /// <param name="cancellationToken"></param>
 444        /// <returns><see cref="IList&lt;RuleDescription&gt;"/> containing list of rules.</returns>
 445        /// <exception cref="ArgumentOutOfRangeException">If the parameters are out of range.</exception>
 446        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 447        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 448        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 449        /// <exception cref="ServiceBusException">An internal error or an unexpected exception occured.</exception>
 450        /// <remarks>You can simulate pages of list of entities by manipulating <paramref name="count"/> and <paramref n
 451        /// skip(0)+count(100) gives first 100 entities. skip(100)+count(100) gives the next 100 entities.
 452        /// Note - Only following data types are deserialized in Filters and Action parameters - string,int,long,bool,do
 453        /// Other data types would return its string value.</remarks>
 454        public virtual async Task<IList<RuleDescription>> GetRulesAsync(string topicPath, string subscriptionName, int c
 455        {
 0456            EntityNameHelper.CheckValidTopicName(topicPath);
 0457            EntityNameHelper.CheckValidSubscriptionName(subscriptionName);
 0458            if (count > 100 || count < 1)
 459            {
 0460                throw new ArgumentOutOfRangeException(nameof(count), "Value should be between 1 and 100");
 461            }
 0462            if (skip < 0)
 463            {
 0464                throw new ArgumentOutOfRangeException(nameof(skip), "Value cannot be negative");
 465            }
 466
 0467            var content = await GetEntity($"{topicPath}/Subscriptions/{subscriptionName}/rules", $"$skip={skip}&$top={co
 0468            return RuleDescriptionExtensions.ParseCollectionFromContent(content);
 0469        }
 470
 471        #endregion
 472
 473        #region GetEntitesRuntimeInfo
 474        /// <summary>
 475        /// Retrieves the list of runtime information for queues present in the namespace.
 476        /// </summary>
 477        /// <param name="count">The number of queues to fetch. Defaults to 100. Maximum value allowed is 100.</param>
 478        /// <param name="skip">The number of queues to skip. Defaults to 0. Cannot be negative.</param>
 479        /// <param name="cancellationToken"></param>
 480        /// <returns><see cref="IList&lt;QueueRuntimeInfo&gt;"/> containing list of queue runtime information.</returns>
 481        /// <exception cref="ArgumentOutOfRangeException">If the parameters are out of range.</exception>
 482        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 483        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 484        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 485        /// <exception cref="ServiceBusException">An internal error or an unexpected exception occured.</exception>
 486        /// <remarks>You can simulate pages of list of entities by manipulating <paramref name="count"/> and <paramref n
 487        /// skip(0)+count(100) gives first 100 entities. skip(100)+count(100) gives the next 100 entities.</remarks>
 488        public virtual async Task<IList<QueueRuntimeInfo>> GetQueuesRuntimeInfoAsync(int count = 100, int skip = 0, Canc
 489        {
 0490            if (count > 100 || count < 1)
 491            {
 0492                throw new ArgumentOutOfRangeException(nameof(count), "Value should be between 1 and 100");
 493            }
 0494            if (skip < 0)
 495            {
 0496                throw new ArgumentOutOfRangeException(nameof(skip), "Value cannot be negative");
 497            }
 498
 0499            var content = await GetEntity("$Resources/queues", $"$skip={skip}&$top={count}", false, cancellationToken).C
 0500            return QueueRuntimeInfoExtensions.ParseCollectionFromContent(content);
 0501        }
 502
 503        /// <summary>
 504        /// Retrieves the list of runtime information for topics present in the namespace.
 505        /// </summary>
 506        /// <param name="count">The number of topics to fetch. Defaults to 100. Maximum value allowed is 100.</param>
 507        /// <param name="skip">The number of topics to skip. Defaults to 0. Cannot be negative.</param>
 508        /// <param name="cancellationToken"></param>
 509        /// <returns><see cref="IList&lt;TopicRuntimeInfo&gt;"/> containing list of topics.</returns>
 510        /// <exception cref="ArgumentOutOfRangeException">If the parameters are out of range.</exception>
 511        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 512        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 513        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 514        /// <exception cref="ServiceBusException">An internal error or an unexpected exception occured.</exception>
 515        /// <remarks>You can simulate pages of list of entities by manipulating <paramref name="count"/> and <paramref n
 516        /// skip(0)+count(100) gives first 100 entities. skip(100)+count(100) gives the next 100 entities.</remarks>
 517        public virtual async Task<IList<TopicRuntimeInfo>> GetTopicsRuntimeInfoAsync(int count = 100, int skip = 0, Canc
 518        {
 0519             if (count > 100 || count < 1)
 520             {
 0521                 throw new ArgumentOutOfRangeException(nameof(count), "Value should be between 1 and 100");
 522             }
 0523             if (skip < 0)
 524             {
 0525                 throw new ArgumentOutOfRangeException(nameof(skip), "Value cannot be negative");
 526             }
 527
 0528             var content = await GetEntity("$Resources/topics", $"$skip={skip}&$top={count}", false, cancellationToken).
 529
 0530             return TopicRuntimeInfoExtensions.ParseCollectionFromContent(content);
 0531        }
 532
 533        /// <summary>
 534        /// Retrieves the list of runtime information for subscriptions present in the namespace.
 535        /// </summary>
 536        /// <param name="topicPath">The path of the topic relative to service bus namespace.</param>
 537        /// <param name="count">The number of subscriptions to fetch. Defaults to 100. Maximum value allowed is 100.</pa
 538        /// <param name="skip">The number of subscriptions to skip. Defaults to 0. Cannot be negative.</param>
 539        /// <param name="cancellationToken"></param>
 540        /// <returns><see cref="IList&lt;SubscriptionRuntimeInfo&gt;"/> containing list of topics.</returns>
 541        /// <exception cref="ArgumentOutOfRangeException">If the parameters are out of range.</exception>
 542        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 543        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 544        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 545        /// <exception cref="ServiceBusException">An internal error or an unexpected exception occured.</exception>
 546        /// <remarks>You can simulate pages of list of entities by manipulating <paramref name="count"/> and <paramref n
 547        /// skip(0)+count(100) gives first 100 entities. skip(100)+count(100) gives the next 100 entities.</remarks>
 548        public virtual async Task<IList<SubscriptionRuntimeInfo>> GetSubscriptionsRuntimeInfoAsync(string topicPath, int
 549        {
 0550             if (count > 100 || count < 1)
 551             {
 0552                 throw new ArgumentOutOfRangeException(nameof(count), "Value should be between 1 and 100");
 553             }
 0554             if (skip < 0)
 555             {
 0556                 throw new ArgumentOutOfRangeException(nameof(skip), "Value cannot be negative");
 557             }
 558
 0559             var content = await GetEntity($"{topicPath}/Subscriptions", $"$skip={skip}&$top={count}", false, cancellati
 560
 0561             return SubscriptionRuntimeInfoExtensions.ParseCollectionFromContent(topicPath, content);
 0562        }
 563
 564        #endregion
 565
 566        #region CreateEntity
 567
 568        /// <summary>
 569        /// Creates a new queue in the service namespace with the given name.
 570        /// </summary>
 571        /// <remarks>Throws if a queue already exists. <see cref="QueueDescription"/> for default values of queue proper
 572        /// <param name="queuePath">The name of the queue relative to the service namespace base address.</param>
 573        /// <param name="cancellationToken"></param>
 574        /// <returns>The <see cref="QueueDescription"/> of the newly created queue.</returns>
 575        /// <exception cref="ArgumentNullException">Queue name is null or empty.</exception>
 576        /// <exception cref="ArgumentOutOfRangeException">The length of <paramref name="queuePath"/> is greater than 260
 577        /// <exception cref="MessagingEntityAlreadyExistsException">An entity with the same name exists under the same s
 578        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 579        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 580        /// <exception cref="QuotaExceededException">Either the specified size in the description is not supported or th
 581        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 582        /// <exception cref="ServiceBusException">An internal error or unexpected exception occurs.</exception>
 583        public virtual Task<QueueDescription> CreateQueueAsync(string queuePath, CancellationToken cancellationToken = d
 584        {
 0585            return this.CreateQueueAsync(new QueueDescription(queuePath), cancellationToken);
 586        }
 587
 588        /// <summary>
 589        /// Creates a new queue in the service namespace with the given name.
 590        /// </summary>
 591        /// <remarks>Throws if a queue already exists.</remarks>
 592        /// <param name="queueDescription">A <see cref="QueueDescription"/> object describing the attributes with which 
 593        /// <param name="cancellationToken"></param>
 594        /// <returns>The <see cref="QueueDescription"/> of the newly created queue.</returns>
 595        /// <exception cref="ArgumentNullException">Queue name is null or empty.</exception>
 596        /// <exception cref="MessagingEntityAlreadyExistsException">A queue with the same nameexists under the same serv
 597        /// <exception cref="ServiceBusTimeoutException">The operation times out.</exception>
 598        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 599        /// <exception cref="QuotaExceededException">Either the specified size in the description is not supported or th
 600        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 601        /// <exception cref="ServiceBusException">An internal error or unexpected exception occurs.</exception>
 602        public virtual async Task<QueueDescription> CreateQueueAsync(QueueDescription queueDescription, CancellationToke
 603        {
 0604            queueDescription = queueDescription ?? throw new ArgumentNullException(nameof(queueDescription));
 0605            queueDescription.NormalizeDescription(this.endpointFQDN);
 0606            var atomRequest = queueDescription.Serialize().ToString();
 0607            var content = await PutEntity(
 0608                queueDescription.Path,
 0609                atomRequest,
 0610                false,
 0611                queueDescription.ForwardTo,
 0612                queueDescription.ForwardDeadLetteredMessagesTo,
 0613                cancellationToken).ConfigureAwait(false);
 0614            return QueueDescriptionExtensions.ParseFromContent(content);
 0615        }
 616
 617        /// <summary>
 618        /// Creates a new topic in the service namespace with the given name.
 619        /// </summary>
 620        /// <remarks>Throws if a topic already exists. <see cref="TopicDescription"/> for default values of topic proper
 621        /// <param name="topicPath">The name of the topic relative to the service namespace base address.</param>
 622        /// <param name="cancellationToken"></param>
 623        /// <returns>The <see cref="TopicDescription"/> of the newly created topic.</returns>
 624        /// <exception cref="ArgumentNullException">Topic name is null or empty.</exception>
 625        /// <exception cref="ArgumentOutOfRangeException">The length of <paramref name="topicPath"/> is greater than 260
 626        /// <exception cref="MessagingEntityAlreadyExistsException">A topic with the same name exists under the same ser
 627        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 628        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 629        /// <exception cref="QuotaExceededException">Either the specified size in the description is not supported or th
 630        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 631        /// <exception cref="ServiceBusException">An internal error or unexpected exception occurs.</exception>
 632        public virtual Task<TopicDescription> CreateTopicAsync(string topicPath, CancellationToken cancellationToken = d
 633        {
 0634            return this.CreateTopicAsync(new TopicDescription(topicPath), cancellationToken);
 635        }
 636
 637        /// <summary>
 638        /// Creates a new topic in the service namespace with the given name.
 639        /// </summary>
 640        /// <remarks>Throws if a topic already exists. <see cref="TopicDescription"/> for default values of topic proper
 641        /// <param name="topicDescription">A <see cref="TopicDescription"/> object describing the attributes with which 
 642        /// <param name="cancellationToken"></param>
 643        /// <returns>The <see cref="TopicDescription"/> of the newly created topic.</returns>
 644        /// <exception cref="ArgumentNullException">Topic description is null.</exception>
 645        /// <exception cref="MessagingEntityAlreadyExistsException">A topic with the same name exists under the same ser
 646        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 647        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 648        /// <exception cref="QuotaExceededException">Either the specified size in the description is not supported or th
 649        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 650        /// <exception cref="ServiceBusException">An internal error or unexpected exception occurs.</exception>
 651        public virtual async Task<TopicDescription> CreateTopicAsync(TopicDescription topicDescription, CancellationToke
 652        {
 0653            topicDescription = topicDescription ?? throw new ArgumentNullException(nameof(topicDescription));
 0654            var atomRequest = topicDescription.Serialize().ToString();
 655
 0656            var content = await PutEntity(topicDescription.Path, atomRequest, false, null, null, cancellationToken).Conf
 657
 0658            return TopicDescriptionExtensions.ParseFromContent(content);
 0659        }
 660
 661        /// <summary>
 662        /// Creates a new subscription within a topic in the service namespace with the given name.
 663        /// </summary>
 664        /// <remarks>Throws if a subscription already exists. <see cref="SubscriptionDescription"/> for default values o
 665        /// Be default, A "pass-through" filter is created for this subscription, which means it will allow all messages
 666        /// <see cref="CreateSubscriptionAsync(SubscriptionDescription, RuleDescription, CancellationToken)"/> for creat
 667        /// <param name="topicPath">The path of the topic relative to the service namespace base address.</param>
 668        /// <param name="subscriptionName">The name of the subscription.</param>
 669        /// <param name="cancellationToken"></param>
 670        /// <returns>The <see cref="SubscriptionDescription"/> of the newly created subscription.</returns>
 671        /// <exception cref="ArgumentNullException">Topic path or subscription name is null or empty.</exception>
 672        /// <exception cref="ArgumentOutOfRangeException">The length of <paramref name="topicPath"/> is greater than 260
 673        /// <exception cref="MessagingEntityAlreadyExistsException">A subscription with the same name exists under the s
 674        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 675        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 676        /// <exception cref="QuotaExceededException">Either the specified size in the description is not supported or th
 677        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 678        /// <exception cref="ServiceBusException">An internal error or unexpected exception occurs.</exception>
 679        public virtual Task<SubscriptionDescription> CreateSubscriptionAsync(string topicPath, string subscriptionName, 
 680        {
 0681            return this.CreateSubscriptionAsync(new SubscriptionDescription(topicPath, subscriptionName), cancellationTo
 682        }
 683
 684        /// <summary>
 685        /// Creates a new subscription within a topic in the service namespace with the given name.
 686        /// </summary>
 687        /// <remarks>Throws if a subscription already exists.
 688        /// Be default, A "pass-through" filter is created for this subscription, which means it will allow all messages
 689        /// <see cref="CreateSubscriptionAsync(SubscriptionDescription, RuleDescription, CancellationToken)"/> for creat
 690        /// <param name="subscriptionDescription">A <see cref="SubscriptionDescription"/> object describing the attribut
 691        /// <param name="cancellationToken"></param>
 692        /// <returns>The <see cref="SubscriptionDescription"/> of the newly created subscription.</returns>
 693        /// <exception cref="ArgumentNullException">Subscription description is null.</exception>
 694        /// <exception cref="MessagingEntityAlreadyExistsException">A subscription with the same name exists under the s
 695        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 696        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 697        /// <exception cref="QuotaExceededException">Either the specified size in the description is not supported or th
 698        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 699        /// <exception cref="ServiceBusException">An internal error or unexpected exception occurs.</exception>
 700        public virtual Task<SubscriptionDescription> CreateSubscriptionAsync(SubscriptionDescription subscriptionDescrip
 701        {
 0702            subscriptionDescription = subscriptionDescription ?? throw new ArgumentNullException(nameof(subscriptionDesc
 0703            return this.CreateSubscriptionAsync(subscriptionDescription, null, cancellationToken);
 704        }
 705
 706        /// <summary>
 707        /// Creates a new subscription within a topic with the provided default rule.
 708        /// </summary>
 709        /// <remarks>Throws if a subscription already exists. </remarks>
 710        /// <param name="subscriptionDescription">A <see cref="SubscriptionDescription"/> object describing the attribut
 711        /// <param name="defaultRule"> A <see cref="RuleDescription"/> object describing the default rule. If null, then
 712        /// <param name="cancellationToken"></param>
 713        /// <returns>The <see cref="SubscriptionDescription"/> of the newly created subscription.</returns>
 714        /// <exception cref="ArgumentNullException">Subscription description is null.</exception>
 715        /// <exception cref="MessagingEntityAlreadyExistsException">A subscription with the same name exists under the s
 716        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 717        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 718        /// <exception cref="QuotaExceededException">Either the specified size in the description is not supported or th
 719        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 720        /// <exception cref="ServiceBusException">An internal error or unexpected exception occurs.</exception>
 721        public virtual async Task<SubscriptionDescription> CreateSubscriptionAsync(SubscriptionDescription subscriptionD
 722        {
 0723            subscriptionDescription = subscriptionDescription ?? throw new ArgumentNullException(nameof(subscriptionDesc
 0724            subscriptionDescription.NormalizeDescription(this.endpointFQDN);
 0725            subscriptionDescription.DefaultRuleDescription = defaultRule;
 0726            var atomRequest = subscriptionDescription.Serialize().ToString();
 0727            var content = await PutEntity(
 0728                EntityNameHelper.FormatSubscriptionPath(subscriptionDescription.TopicPath, subscriptionDescription.Subsc
 0729                atomRequest,
 0730                false,
 0731                subscriptionDescription.ForwardTo,
 0732                subscriptionDescription.ForwardDeadLetteredMessagesTo,
 0733                cancellationToken).ConfigureAwait(false);
 0734            return SubscriptionDescriptionExtensions.ParseFromContent(subscriptionDescription.TopicPath, content);
 0735        }
 736
 737        /// <summary>
 738        /// Adds a new rule to the subscription under given topic.
 739        /// </summary>
 740        /// <param name="topicPath">The topic path relative to the service namespace base address.</param>
 741        /// <param name="subscriptionName">The name of the subscription.</param>
 742        /// <param name="ruleDescription">A <see cref="RuleDescription"/> object describing the attributes with which th
 743        /// <param name="cancellationToken"></param>
 744        /// <exception cref="ArgumentNullException">Subscription or rule description is null.</exception>
 745        /// <exception cref="MessagingEntityAlreadyExistsException">A subscription with the same name exists under the s
 746        /// <exception cref="ServiceBusTimeoutException">The operation times out. The timeout period is initialized thro
 747        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 748        /// <exception cref="QuotaExceededException">Either the specified size in the description is not supported or th
 749        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 750        /// <exception cref="ServiceBusException">An internal error or unexpected exception occurs.</exception>
 751        /// <returns><see cref="RuleDescription"/> of the recently created rule.</returns>
 752        public virtual async Task<RuleDescription> CreateRuleAsync(string topicPath, string subscriptionName, RuleDescri
 753        {
 0754            EntityNameHelper.CheckValidTopicName(topicPath);
 0755            EntityNameHelper.CheckValidSubscriptionName(subscriptionName);
 0756            ruleDescription = ruleDescription ?? throw new ArgumentNullException(nameof(ruleDescription));
 757
 0758            var atomRequest = ruleDescription.Serialize().ToString();
 759
 0760            var content = await PutEntity(
 0761                EntityNameHelper.FormatRulePath(topicPath, subscriptionName, ruleDescription.Name),
 0762                atomRequest,
 0763                false,
 0764                null,
 0765                null,
 0766                cancellationToken).ConfigureAwait(false);
 767
 0768            return RuleDescriptionExtensions.ParseFromContent(content);
 0769        }
 770
 771        #endregion CreateEntity
 772
 773        #region UpdateEntity
 774        /// <summary>
 775        /// Updates an existing queue.
 776        /// </summary>
 777        /// <param name="queueDescription">A <see cref="QueueDescription"/> object describing the attributes with which 
 778        /// <param name="cancellationToken"></param>
 779        /// <returns>The <see cref="QueueDescription"/> of the updated queue.</returns>
 780        /// <exception cref="ArgumentNullException">Queue descriptor is null.</exception>
 781        /// <exception cref="MessagingEntityNotFoundException">Described queue was not found.</exception>
 782        /// <exception cref="ServiceBusTimeoutException">The operation times out.</exception>
 783        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 784        /// <exception cref="QuotaExceededException">Either the specified size in the description is not supported or th
 785        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 786        /// <exception cref="ServiceBusException">An internal error or unexpected exception occurs.</exception>
 787        public virtual async Task<QueueDescription> UpdateQueueAsync(QueueDescription queueDescription, CancellationToke
 788        {
 0789            queueDescription = queueDescription ?? throw new ArgumentNullException(nameof(queueDescription));
 0790            queueDescription.NormalizeDescription(this.endpointFQDN);
 791
 0792            var atomRequest = queueDescription.Serialize().ToString();
 793
 0794            var content = await PutEntity(
 0795                queueDescription.Path,
 0796                atomRequest,
 0797                true,
 0798                queueDescription.ForwardTo,
 0799                queueDescription.ForwardDeadLetteredMessagesTo,
 0800                cancellationToken).ConfigureAwait(false);
 801
 0802            return QueueDescriptionExtensions.ParseFromContent(content);
 0803        }
 804
 805        /// <summary>
 806        /// Updates an existing topic.
 807        /// </summary>
 808        /// <param name="topicDescription">A <see cref="TopicDescription"/> object describing the attributes with which 
 809        /// <param name="cancellationToken"></param>
 810        /// <returns>The <see cref="TopicDescription"/> of the updated topic.</returns>
 811        /// <exception cref="ArgumentNullException">Topic descriptor is null.</exception>
 812        /// <exception cref="MessagingEntityNotFoundException">Described topic was not found.</exception>
 813        /// <exception cref="ServiceBusTimeoutException">The operation times out.</exception>
 814        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 815        /// <exception cref="QuotaExceededException">Either the specified size in the description is not supported or th
 816        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 817        /// <exception cref="ServiceBusException">An internal error or unexpected exception occurs.</exception>
 818        public virtual async Task<TopicDescription> UpdateTopicAsync(TopicDescription topicDescription, CancellationToke
 819        {
 0820            topicDescription = topicDescription ?? throw new ArgumentNullException(nameof(topicDescription));
 0821            var atomRequest = topicDescription.Serialize().ToString();
 822
 0823            var content = await PutEntity(topicDescription.Path, atomRequest, true, null, null, cancellationToken).Confi
 824
 0825            return TopicDescriptionExtensions.ParseFromContent(content);
 0826        }
 827
 828        /// <summary>
 829        /// Updates an existing subscription under a topic.
 830        /// </summary>
 831        /// <param name="subscriptionDescription">A <see cref="SubscriptionDescription"/> object describing the attribut
 832        /// <param name="cancellationToken"></param>
 833        /// <returns>The <see cref="SubscriptionDescription"/> of the updated subscription.</returns>
 834        /// <exception cref="ArgumentNullException">subscription descriptor is null.</exception>
 835        /// <exception cref="MessagingEntityNotFoundException">Described subscription was not found.</exception>
 836        /// <exception cref="ServiceBusTimeoutException">The operation times out.</exception>
 837        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 838        /// <exception cref="QuotaExceededException">Either the specified size in the description is not supported or th
 839        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 840        /// <exception cref="ServiceBusException">An internal error or unexpected exception occurs.</exception>
 841        public virtual async Task<SubscriptionDescription> UpdateSubscriptionAsync(SubscriptionDescription subscriptionD
 842        {
 0843            subscriptionDescription = subscriptionDescription ?? throw new ArgumentNullException(nameof(subscriptionDesc
 0844            subscriptionDescription.NormalizeDescription(this.endpointFQDN);
 0845            var atomRequest = subscriptionDescription.Serialize().ToString();
 0846            var content = await PutEntity(
 0847                EntityNameHelper.FormatSubscriptionPath(subscriptionDescription.TopicPath, subscriptionDescription.Subsc
 0848                atomRequest,
 0849                true,
 0850                subscriptionDescription.ForwardTo,
 0851                subscriptionDescription.ForwardDeadLetteredMessagesTo,
 0852                cancellationToken).ConfigureAwait(false);
 0853            return SubscriptionDescriptionExtensions.ParseFromContent(subscriptionDescription.TopicPath, content);
 0854        }
 855
 856        /// <summary>
 857        /// Updates an existing rule for a topic-subscription.
 858        /// </summary>
 859        /// <param name="topicPath">Path of the topic.</param>
 860        /// <param name="subscriptionName">Name of the subscription.</param>
 861        /// <param name="ruleDescription">A <see cref="RuleDescription"/> object describing the attributes with which th
 862        /// <param name="cancellationToken"></param>
 863        /// <returns>The <see cref="RuleDescription"/> of the updated rule.</returns>
 864        /// <exception cref="ArgumentNullException">rule descriptor is null.</exception>
 865        /// <exception cref="MessagingEntityNotFoundException">Described topic/subscription/rule was not found.</excepti
 866        /// <exception cref="ServiceBusTimeoutException">The operation times out.</exception>
 867        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 868        /// <exception cref="QuotaExceededException">Either the specified size in the description is not supported or th
 869        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 870        /// <exception cref="ServiceBusException">An internal error or unexpected exception occurs.</exception>
 871        public virtual async Task<RuleDescription> UpdateRuleAsync(string topicPath, string subscriptionName, RuleDescri
 872        {
 0873            ruleDescription = ruleDescription ?? throw new ArgumentNullException(nameof(ruleDescription));
 0874            EntityNameHelper.CheckValidTopicName(topicPath);
 0875            EntityNameHelper.CheckValidSubscriptionName(subscriptionName);
 876
 0877            var atomRequest = ruleDescription.Serialize().ToString();
 0878            var content = await PutEntity(
 0879                EntityNameHelper.FormatRulePath(topicPath, subscriptionName, ruleDescription.Name),
 0880                atomRequest,
 0881                true,
 0882                null, null,
 0883                cancellationToken).ConfigureAwait(false);
 884
 0885            return RuleDescriptionExtensions.ParseFromContent(content);
 0886        }
 887
 888        #endregion
 889
 890        #region Exists
 891        /// <summary>
 892        /// Checks whether a given queue exists or not.
 893        /// </summary>
 894        /// <param name="queuePath">Path of the queue entity to check.</param>
 895        /// <param name="cancellationToken"></param>
 896        /// <returns>True if queue exists, false otherwise.</returns>
 897        /// <exception cref="ArgumentException">Queue path provided is not valid.</exception>
 898        /// <exception cref="ServiceBusTimeoutException">The operation times out.</exception>
 899        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 900        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 901        /// <exception cref="ServiceBusException">An internal error or unexpected exception occurs.</exception>
 902        public virtual async Task<bool> QueueExistsAsync(string queuePath, CancellationToken cancellationToken = default
 903        {
 0904            EntityNameHelper.CheckValidQueueName(queuePath);
 905
 906            try
 907            {
 908                // TODO: Optimize by removing deserialization costs.
 0909                var qd = await GetQueueAsync(queuePath, cancellationToken).ConfigureAwait(false);
 0910            }
 0911            catch (MessagingEntityNotFoundException)
 912            {
 0913                return false;
 914            }
 915
 0916            return true;
 0917        }
 918
 919        /// <summary>
 920        /// Checks whether a given topic exists or not.
 921        /// </summary>
 922        /// <param name="topicPath">Path of the topic entity to check.</param>
 923        /// <param name="cancellationToken"></param>
 924        /// <returns>True if topic exists, false otherwise.</returns>
 925        /// <exception cref="ArgumentException">topic path provided is not valid.</exception>
 926        /// <exception cref="ServiceBusTimeoutException">The operation times out.</exception>
 927        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 928        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 929        /// <exception cref="ServiceBusException">An internal error or unexpected exception occurs.</exception>
 930        public virtual async Task<bool> TopicExistsAsync(string topicPath, CancellationToken cancellationToken = default
 931        {
 0932            EntityNameHelper.CheckValidTopicName(topicPath);
 933
 934            try
 935            {
 936                // TODO: Optimize by removing deserialization costs.
 0937                var td = await GetTopicAsync(topicPath, cancellationToken).ConfigureAwait(false);
 0938            }
 0939            catch (MessagingEntityNotFoundException)
 940            {
 0941                return false;
 942            }
 943
 0944            return true;
 0945        }
 946
 947        /// <summary>
 948        /// Checks whether a given subscription exists or not.
 949        /// </summary>
 950        /// <param name="topicPath">Path of the topic.</param>
 951        /// <param name="subscriptionName">Name of the subscription to check.</param>
 952        /// <param name="cancellationToken"></param>
 953        /// <returns>True if subscription exists, false otherwise.</returns>
 954        /// <exception cref="ArgumentException">topic or subscription path provided is not valid.</exception>
 955        /// <exception cref="ServiceBusTimeoutException">The operation times out.</exception>
 956        /// <exception cref="UnauthorizedAccessException">No sufficient permission to perform this operation. You should
 957        /// <exception cref="ServerBusyException">The server is busy. You should wait before you retry the operation.</e
 958        /// <exception cref="ServiceBusException">An internal error or unexpected exception occurs.</exception>
 959        public virtual async Task<bool> SubscriptionExistsAsync(string topicPath, string subscriptionName, CancellationT
 960        {
 0961            EntityNameHelper.CheckValidTopicName(topicPath);
 0962            EntityNameHelper.CheckValidSubscriptionName(subscriptionName);
 963
 964            try
 965            {
 966                // TODO: Optimize by removing deserialization costs.
 0967                var sd = await GetSubscriptionAsync(topicPath, subscriptionName, cancellationToken).ConfigureAwait(false
 0968            }
 0969            catch (MessagingEntityNotFoundException)
 970            {
 0971                return false;
 972            }
 973
 0974            return true;
 0975        }
 976
 977        public Task CloseAsync()
 978        {
 0979            httpClient?.Dispose();
 0980            httpClient = null;
 981
 0982            return Task.CompletedTask;
 983        }
 984
 985        #endregion
 986
 987        private static int GetPort(string endpoint)
 988        {
 989            // used for internal testing
 6990            if (endpoint.EndsWith("onebox.windows-int.net", StringComparison.InvariantCultureIgnoreCase))
 991            {
 0992                return 4446;
 993            }
 994
 6995            return -1;
 996        }
 997
 998        private static async Task<Exception> ValidateHttpResponse(HttpResponseMessage response)
 999        {
 01000            if (response.IsSuccessStatusCode)
 1001            {
 01002                return null;
 1003            }
 1004
 01005            var exceptionMessage = await (response.Content?.ReadAsStringAsync() ?? Task.FromResult(string.Empty));
 01006            exceptionMessage = ParseDetailIfAvailable(exceptionMessage) ?? response.ReasonPhrase;
 1007
 01008            if (response.StatusCode == HttpStatusCode.Unauthorized)
 1009            {
 01010                return new UnauthorizedException(exceptionMessage);
 1011            }
 1012
 01013            if (response.StatusCode == HttpStatusCode.NotFound || response.StatusCode == HttpStatusCode.NoContent)
 1014            {
 01015                return new MessagingEntityNotFoundException(exceptionMessage);
 1016            }
 1017
 01018            if (response.StatusCode == HttpStatusCode.Conflict)
 1019            {
 01020                if (response.RequestMessage.Method.Equals(HttpMethod.Delete))
 1021                {
 01022                    return new ServiceBusException(true, exceptionMessage);
 1023                }
 1024
 01025                if (response.RequestMessage.Method.Equals(HttpMethod.Put) && response.RequestMessage.Headers.IfMatch.Cou
 1026                {
 1027                    // response.RequestMessage.Headers.IfMatch.Count > 0 is true for UpdateEntity scenario
 01028                    return new ServiceBusException(true, exceptionMessage);
 1029                }
 1030
 01031                if (exceptionMessage.Contains(ManagementClientConstants.ConflictOperationInProgressSubCode))
 1032                {
 01033                    return new ServiceBusException(true, exceptionMessage);
 1034                }
 1035
 01036                return new MessagingEntityAlreadyExistsException(exceptionMessage);
 1037            }
 1038
 01039            if (response.StatusCode == HttpStatusCode.Forbidden)
 1040            {
 01041                if (exceptionMessage.Contains(ManagementClientConstants.ForbiddenInvalidOperationSubCode))
 1042                {
 01043                    return new InvalidOperationException(exceptionMessage);
 1044                }
 1045
 01046                return new QuotaExceededException(exceptionMessage);
 1047            }
 1048
 01049            if (response.StatusCode == HttpStatusCode.BadRequest)
 1050            {
 01051                return new ServiceBusException(false, new ArgumentException(exceptionMessage));
 1052            }
 1053
 01054            if (response.StatusCode == HttpStatusCode.ServiceUnavailable)
 1055            {
 01056                return new ServerBusyException(exceptionMessage);
 1057            }
 1058
 01059            return new ServiceBusException(true, exceptionMessage + "; response status code: " + response.StatusCode);
 01060        }
 1061
 1062        private static string ParseDetailIfAvailable(string content)
 1063        {
 01064            if (string.IsNullOrWhiteSpace(content))
 1065            {
 01066                return null;
 1067            }
 1068
 1069            try
 1070            {
 01071                var errorContentXml = XElement.Parse(content);
 01072                var detail = errorContentXml.Element("Detail");
 1073
 01074                return detail?.Value ?? content;
 1075            }
 01076            catch (Exception)
 1077            {
 01078                return content;
 1079            }
 01080        }
 1081
 1082        private static ITokenProvider CreateTokenProvider(ServiceBusConnectionStringBuilder builder)
 1083        {
 121084            if (builder.SasToken != null)
 1085            {
 01086                return new SharedAccessSignatureTokenProvider(builder.SasToken);
 1087            }
 121088            else if (builder.SasKeyName != null && builder.SasKey != null)
 1089            {
 01090                return new SharedAccessSignatureTokenProvider(builder.SasKeyName, builder.SasKey);
 1091            }
 121092            else if (builder.Authentication.Equals(ServiceBusConnectionStringBuilder.AuthenticationType.ManagedIdentity)
 1093            {
 61094                return new ManagedIdentityTokenProvider();
 1095            }
 1096
 61097            throw new ArgumentException("Could not create token provider. Either ITokenProvider has to be passed into co
 1098        }
 1099
 1100        private Task<string> GetToken(Uri requestUri)
 1101        {
 01102            return this.GetToken(requestUri.GetLeftPart(UriPartial.Path));
 1103        }
 1104
 1105        private async Task<string> GetToken(string requestUri)
 1106        {
 01107            var token = await this.tokenProvider.GetTokenAsync(requestUri, TimeSpan.FromHours(1)).ConfigureAwait(false);
 01108            return token.TokenValue;
 01109        }
 1110
 1111        private async Task<string> GetEntity(string path, string query, bool enrich, CancellationToken cancellationToken
 1112        {
 01113            MessagingEventSource.Log.ManagementOperationStart(this.clientId, nameof(GetEntity), $"path:{path},query:{que
 1114
 01115            var queryString = $"{ManagementClientConstants.apiVersionQuery}&enrich={enrich}";
 01116            if (query != null)
 1117            {
 01118                queryString = queryString + "&" + query;
 1119            }
 01120            var uri = new UriBuilder(this.endpointFQDN)
 01121            {
 01122                Path = path,
 01123                Scheme = Uri.UriSchemeHttps,
 01124                Port = this.port,
 01125                Query = queryString
 01126            }.Uri;
 1127
 01128            var request = new HttpRequestMessage(HttpMethod.Get, uri);
 01129            HttpResponseMessage response = await SendHttpRequest(request, cancellationToken).ConfigureAwait(false);
 01130            var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
 1131
 01132            MessagingEventSource.Log.ManagementOperationEnd(this.clientId, nameof(GetEntity), $"path:{path},query:{query
 01133            return result;
 01134        }
 1135
 1136        private async Task<string> PutEntity(string path, string requestBody, bool isUpdate, string forwardTo, string fw
 1137        {
 01138            MessagingEventSource.Log.ManagementOperationStart(this.clientId, nameof(PutEntity), $"path:{path},isUpdate:{
 1139
 01140            var uri = new UriBuilder(this.endpointFQDN)
 01141            {
 01142                Path = path,
 01143                Port = this.port,
 01144                Scheme = Uri.UriSchemeHttps,
 01145                Query = $"{ManagementClientConstants.apiVersionQuery}"
 01146            }.Uri;
 1147
 01148            var request = new HttpRequestMessage(HttpMethod.Put, uri);
 01149            request.Content = new StringContent(
 01150                requestBody,
 01151                Encoding.UTF8,
 01152                ManagementClientConstants.AtomContentType
 01153            );
 1154
 01155            if (isUpdate)
 1156            {
 01157                request.Headers.Add("If-Match", "*");
 1158            }
 1159
 01160            if (!string.IsNullOrWhiteSpace(forwardTo))
 1161            {
 01162                var token = await this.GetToken(forwardTo).ConfigureAwait(false);
 01163                request.Headers.Add(ManagementClientConstants.ServiceBusSupplementartyAuthorizationHeaderName, token);
 1164            }
 1165
 01166            if (!string.IsNullOrWhiteSpace(fwdDeadLetterTo))
 1167            {
 01168                var token = await this.GetToken(fwdDeadLetterTo).ConfigureAwait(false);
 01169                request.Headers.Add(ManagementClientConstants.ServiceBusDlqSupplementaryAuthorizationHeaderName, token);
 1170            }
 1171
 01172            HttpResponseMessage response = await SendHttpRequest(request, cancellationToken).ConfigureAwait(false);
 01173            var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
 1174
 01175            MessagingEventSource.Log.ManagementOperationEnd(this.clientId, nameof(PutEntity), $"path:{path},isUpdate:{is
 01176            return result;
 01177        }
 1178
 1179        private async Task DeleteEntity(string path, CancellationToken cancellationToken)
 1180        {
 01181            MessagingEventSource.Log.ManagementOperationStart(this.clientId, nameof(DeleteEntity), path);
 1182
 01183            var uri = new UriBuilder(this.endpointFQDN)
 01184            {
 01185                Path = path,
 01186                Scheme = Uri.UriSchemeHttps,
 01187                Port = this.port,
 01188                Query = ManagementClientConstants.apiVersionQuery
 01189            }.Uri;
 1190
 01191            var request = new HttpRequestMessage(HttpMethod.Delete, uri);
 01192            await SendHttpRequest(request, cancellationToken).ConfigureAwait(false);
 01193            MessagingEventSource.Log.ManagementOperationEnd(this.clientId, nameof(DeleteEntity), path);
 01194        }
 1195
 1196        private async Task<HttpResponseMessage> SendHttpRequest(HttpRequestMessage request, CancellationToken cancellati
 1197        {
 01198            if (request.Headers.Authorization == null)
 1199            {
 1200                // First attempt.
 01201                var token = await this.GetToken(request.RequestUri).ConfigureAwait(false);
 01202                request.Headers.Add("Authorization", token);
 01203                request.Headers.Add("UserAgent", $"SERVICEBUS/{ManagementClientConstants.ApiVersion}(api-origin={ClientI
 1204            }
 1205            else
 1206            {
 1207                // This is a retried request.
 01208                request = CloneRequest(request);
 1209            }
 1210
 1211            HttpResponseMessage response;
 1212            try
 1213            {
 01214                response = await this.httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
 01215            }
 01216            catch (HttpRequestException exception)
 1217            {
 01218                MessagingEventSource.Log.ManagementOperationException(this.clientId, nameof(SendHttpRequest), exception)
 01219                throw new ServiceBusException(true, exception);
 1220            }
 1221
 01222            var exceptionReturned = await ValidateHttpResponse(response).ConfigureAwait(false);
 01223            if (exceptionReturned == null)
 1224            {
 01225                return response;
 1226            }
 1227            else
 1228            {
 01229                MessagingEventSource.Log.ManagementOperationException(this.clientId, nameof(SendHttpRequest), exceptionR
 01230                throw exceptionReturned;
 1231            }
 01232        }
 1233    }
 1234}