| | | 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 | | |
| | | 4 | | namespace 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) |
| | 0 | 29 | | : this(new ServiceBusConnectionStringBuilder(connectionString)) |
| | | 30 | | { |
| | 0 | 31 | | } |
| | | 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) |
| | 0 | 39 | | : this(new ServiceBusConnectionStringBuilder {Endpoint = endpoint}, tokenProvider) |
| | | 40 | | { |
| | 0 | 41 | | } |
| | | 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> |
| | 12 | 48 | | public ManagementClient(ServiceBusConnectionStringBuilder connectionStringBuilder, ITokenProvider tokenProvider |
| | | 49 | | { |
| | 12 | 50 | | this.httpClient = new HttpClient { Timeout = connectionStringBuilder.OperationTimeout }; |
| | 12 | 51 | | this.endpointFQDN = connectionStringBuilder.Endpoint; |
| | 12 | 52 | | this.tokenProvider = tokenProvider ?? CreateTokenProvider(connectionStringBuilder); |
| | 6 | 53 | | this.port = GetPort(connectionStringBuilder.Endpoint); |
| | 6 | 54 | | this.clientId = nameof(ManagementClient) + Guid.NewGuid().ToString("N").Substring(0, 6); |
| | | 55 | | |
| | 6 | 56 | | MessagingEventSource.Log.ManagementClientCreated(this.clientId, this.httpClient.Timeout.TotalSeconds, this.t |
| | 6 | 57 | | } |
| | | 58 | | |
| | | 59 | | public static HttpRequestMessage CloneRequest(HttpRequestMessage req) |
| | | 60 | | { |
| | 0 | 61 | | HttpRequestMessage clone = new HttpRequestMessage(req.Method, req.RequestUri); |
| | | 62 | | |
| | 0 | 63 | | clone.Content = req.Content; |
| | 0 | 64 | | clone.Version = req.Version; |
| | | 65 | | |
| | 0 | 66 | | foreach (KeyValuePair<string, object> prop in req.Properties) |
| | | 67 | | { |
| | 0 | 68 | | clone.Properties.Add(prop); |
| | | 69 | | } |
| | | 70 | | |
| | 0 | 71 | | foreach (KeyValuePair<string, IEnumerable<string>> header in req.Headers) |
| | | 72 | | { |
| | 0 | 73 | | clone.Headers.TryAddWithoutValidation(header.Key, header.Value); |
| | | 74 | | } |
| | | 75 | | |
| | 0 | 76 | | 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 | | { |
| | 0 | 87 | | var content = await GetEntity("$namespaceinfo", null, false, cancellationToken).ConfigureAwait(false); |
| | 0 | 88 | | return NamespaceInfoExtensions.ParseFromContent(content); |
| | 0 | 89 | | } |
| | | 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 | | { |
| | 0 | 107 | | EntityNameHelper.CheckValidQueueName(queuePath); |
| | 0 | 108 | | 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 | | { |
| | 0 | 125 | | EntityNameHelper.CheckValidTopicName(topicPath); |
| | 0 | 126 | | 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 | | { |
| | 0 | 144 | | EntityNameHelper.CheckValidTopicName(topicPath); |
| | 0 | 145 | | EntityNameHelper.CheckValidSubscriptionName(subscriptionName); |
| | | 146 | | |
| | 0 | 147 | | 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 | | { |
| | 0 | 166 | | EntityNameHelper.CheckValidTopicName(topicPath); |
| | 0 | 167 | | EntityNameHelper.CheckValidSubscriptionName(subscriptionName); |
| | 0 | 168 | | EntityNameHelper.CheckValidRuleName(ruleName); |
| | | 169 | | |
| | 0 | 170 | | 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 | | { |
| | 0 | 192 | | EntityNameHelper.CheckValidQueueName(queuePath); |
| | | 193 | | |
| | 0 | 194 | | var content = await GetEntity(queuePath, null, false, cancellationToken).ConfigureAwait(false); |
| | | 195 | | |
| | 0 | 196 | | return QueueDescriptionExtensions.ParseFromContent(content); |
| | 0 | 197 | | } |
| | | 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 | | { |
| | 0 | 214 | | EntityNameHelper.CheckValidTopicName(topicPath); |
| | | 215 | | |
| | 0 | 216 | | var content = await GetEntity(topicPath, null, false, cancellationToken).ConfigureAwait(false); |
| | | 217 | | |
| | 0 | 218 | | return TopicDescriptionExtensions.ParseFromContent(content); |
| | 0 | 219 | | } |
| | | 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 | | { |
| | 0 | 237 | | EntityNameHelper.CheckValidTopicName(topicPath); |
| | 0 | 238 | | EntityNameHelper.CheckValidSubscriptionName(subscriptionName); |
| | | 239 | | |
| | 0 | 240 | | var content = await GetEntity(EntityNameHelper.FormatSubscriptionPath(topicPath, subscriptionName), null, fa |
| | | 241 | | |
| | 0 | 242 | | return SubscriptionDescriptionExtensions.ParseFromContent(topicPath, content); |
| | 0 | 243 | | } |
| | | 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 | | { |
| | 0 | 264 | | EntityNameHelper.CheckValidTopicName(topicPath); |
| | 0 | 265 | | EntityNameHelper.CheckValidSubscriptionName(subscriptionName); |
| | 0 | 266 | | EntityNameHelper.CheckValidRuleName(ruleName); |
| | | 267 | | |
| | 0 | 268 | | var content = await GetEntity(EntityNameHelper.FormatRulePath(topicPath, subscriptionName, ruleName), null, |
| | | 269 | | |
| | 0 | 270 | | return RuleDescriptionExtensions.ParseFromContent(content); |
| | 0 | 271 | | } |
| | | 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 | | { |
| | 0 | 291 | | EntityNameHelper.CheckValidQueueName(queuePath); |
| | | 292 | | |
| | 0 | 293 | | var content = await GetEntity(queuePath, null, true, cancellationToken).ConfigureAwait(false); |
| | | 294 | | |
| | 0 | 295 | | return QueueRuntimeInfoExtensions.ParseFromContent(content); |
| | 0 | 296 | | } |
| | | 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 | | { |
| | 0 | 313 | | EntityNameHelper.CheckValidTopicName(topicPath); |
| | | 314 | | |
| | 0 | 315 | | var content = await GetEntity(topicPath, null, true, cancellationToken).ConfigureAwait(false); |
| | | 316 | | |
| | 0 | 317 | | return TopicRuntimeInfoExtensions.ParseFromContent(content); |
| | 0 | 318 | | } |
| | | 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 | | { |
| | 0 | 336 | | EntityNameHelper.CheckValidTopicName(topicPath); |
| | 0 | 337 | | EntityNameHelper.CheckValidSubscriptionName(subscriptionName); |
| | | 338 | | |
| | 0 | 339 | | var content = await GetEntity(EntityNameHelper.FormatSubscriptionPath(topicPath, subscriptionName), null, tr |
| | | 340 | | |
| | 0 | 341 | | return SubscriptionRuntimeInfoExtensions.ParseFromContent(topicPath, content); |
| | 0 | 342 | | } |
| | | 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<QueueDescription>"/> 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 | | { |
| | 0 | 363 | | if (count > 100 || count < 1) |
| | | 364 | | { |
| | 0 | 365 | | throw new ArgumentOutOfRangeException(nameof(count), "Value should be between 1 and 100"); |
| | | 366 | | } |
| | 0 | 367 | | if (skip < 0) |
| | | 368 | | { |
| | 0 | 369 | | throw new ArgumentOutOfRangeException(nameof(skip), "Value cannot be negative"); |
| | | 370 | | } |
| | | 371 | | |
| | 0 | 372 | | var content = await GetEntity("$Resources/queues", $"$skip={skip}&$top={count}", false, cancellationToken).C |
| | 0 | 373 | | return QueueDescriptionExtensions.ParseCollectionFromContent(content); |
| | 0 | 374 | | } |
| | | 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<TopicDescription>"/> 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 | | { |
| | 0 | 392 | | if (count > 100 || count < 1) |
| | | 393 | | { |
| | 0 | 394 | | throw new ArgumentOutOfRangeException(nameof(count), "Value should be between 1 and 100"); |
| | | 395 | | } |
| | 0 | 396 | | if (skip < 0) |
| | | 397 | | { |
| | 0 | 398 | | throw new ArgumentOutOfRangeException(nameof(skip), "Value cannot be negative"); |
| | | 399 | | } |
| | | 400 | | |
| | 0 | 401 | | var content = await GetEntity("$Resources/topics", $"$skip={skip}&$top={count}", false, cancellationToken).C |
| | 0 | 402 | | return TopicDescriptionExtensions.ParseCollectionFromContent(content); |
| | 0 | 403 | | } |
| | | 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<SubscriptionDescription>"/> 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 | | { |
| | 0 | 422 | | EntityNameHelper.CheckValidTopicName(topicPath); |
| | 0 | 423 | | if (count > 100 || count < 1) |
| | | 424 | | { |
| | 0 | 425 | | throw new ArgumentOutOfRangeException(nameof(count), "Value should be between 1 and 100"); |
| | | 426 | | } |
| | 0 | 427 | | if (skip < 0) |
| | | 428 | | { |
| | 0 | 429 | | throw new ArgumentOutOfRangeException(nameof(skip), "Value cannot be negative"); |
| | | 430 | | } |
| | | 431 | | |
| | 0 | 432 | | var content = await GetEntity($"{topicPath}/Subscriptions", $"$skip={skip}&$top={count}", false, cancellatio |
| | 0 | 433 | | return SubscriptionDescriptionExtensions.ParseCollectionFromContent(topicPath, content); |
| | 0 | 434 | | } |
| | | 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<RuleDescription>"/> 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 | | { |
| | 0 | 456 | | EntityNameHelper.CheckValidTopicName(topicPath); |
| | 0 | 457 | | EntityNameHelper.CheckValidSubscriptionName(subscriptionName); |
| | 0 | 458 | | if (count > 100 || count < 1) |
| | | 459 | | { |
| | 0 | 460 | | throw new ArgumentOutOfRangeException(nameof(count), "Value should be between 1 and 100"); |
| | | 461 | | } |
| | 0 | 462 | | if (skip < 0) |
| | | 463 | | { |
| | 0 | 464 | | throw new ArgumentOutOfRangeException(nameof(skip), "Value cannot be negative"); |
| | | 465 | | } |
| | | 466 | | |
| | 0 | 467 | | var content = await GetEntity($"{topicPath}/Subscriptions/{subscriptionName}/rules", $"$skip={skip}&$top={co |
| | 0 | 468 | | return RuleDescriptionExtensions.ParseCollectionFromContent(content); |
| | 0 | 469 | | } |
| | | 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<QueueRuntimeInfo>"/> 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 | | { |
| | 0 | 490 | | if (count > 100 || count < 1) |
| | | 491 | | { |
| | 0 | 492 | | throw new ArgumentOutOfRangeException(nameof(count), "Value should be between 1 and 100"); |
| | | 493 | | } |
| | 0 | 494 | | if (skip < 0) |
| | | 495 | | { |
| | 0 | 496 | | throw new ArgumentOutOfRangeException(nameof(skip), "Value cannot be negative"); |
| | | 497 | | } |
| | | 498 | | |
| | 0 | 499 | | var content = await GetEntity("$Resources/queues", $"$skip={skip}&$top={count}", false, cancellationToken).C |
| | 0 | 500 | | return QueueRuntimeInfoExtensions.ParseCollectionFromContent(content); |
| | 0 | 501 | | } |
| | | 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<TopicRuntimeInfo>"/> 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 | | { |
| | 0 | 519 | | if (count > 100 || count < 1) |
| | | 520 | | { |
| | 0 | 521 | | throw new ArgumentOutOfRangeException(nameof(count), "Value should be between 1 and 100"); |
| | | 522 | | } |
| | 0 | 523 | | if (skip < 0) |
| | | 524 | | { |
| | 0 | 525 | | throw new ArgumentOutOfRangeException(nameof(skip), "Value cannot be negative"); |
| | | 526 | | } |
| | | 527 | | |
| | 0 | 528 | | var content = await GetEntity("$Resources/topics", $"$skip={skip}&$top={count}", false, cancellationToken). |
| | | 529 | | |
| | 0 | 530 | | return TopicRuntimeInfoExtensions.ParseCollectionFromContent(content); |
| | 0 | 531 | | } |
| | | 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<SubscriptionRuntimeInfo>"/> 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 | | { |
| | 0 | 550 | | if (count > 100 || count < 1) |
| | | 551 | | { |
| | 0 | 552 | | throw new ArgumentOutOfRangeException(nameof(count), "Value should be between 1 and 100"); |
| | | 553 | | } |
| | 0 | 554 | | if (skip < 0) |
| | | 555 | | { |
| | 0 | 556 | | throw new ArgumentOutOfRangeException(nameof(skip), "Value cannot be negative"); |
| | | 557 | | } |
| | | 558 | | |
| | 0 | 559 | | var content = await GetEntity($"{topicPath}/Subscriptions", $"$skip={skip}&$top={count}", false, cancellati |
| | | 560 | | |
| | 0 | 561 | | return SubscriptionRuntimeInfoExtensions.ParseCollectionFromContent(topicPath, content); |
| | 0 | 562 | | } |
| | | 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 | | { |
| | 0 | 585 | | 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 | | { |
| | 0 | 604 | | queueDescription = queueDescription ?? throw new ArgumentNullException(nameof(queueDescription)); |
| | 0 | 605 | | queueDescription.NormalizeDescription(this.endpointFQDN); |
| | 0 | 606 | | var atomRequest = queueDescription.Serialize().ToString(); |
| | 0 | 607 | | var content = await PutEntity( |
| | 0 | 608 | | queueDescription.Path, |
| | 0 | 609 | | atomRequest, |
| | 0 | 610 | | false, |
| | 0 | 611 | | queueDescription.ForwardTo, |
| | 0 | 612 | | queueDescription.ForwardDeadLetteredMessagesTo, |
| | 0 | 613 | | cancellationToken).ConfigureAwait(false); |
| | 0 | 614 | | return QueueDescriptionExtensions.ParseFromContent(content); |
| | 0 | 615 | | } |
| | | 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 | | { |
| | 0 | 634 | | 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 | | { |
| | 0 | 653 | | topicDescription = topicDescription ?? throw new ArgumentNullException(nameof(topicDescription)); |
| | 0 | 654 | | var atomRequest = topicDescription.Serialize().ToString(); |
| | | 655 | | |
| | 0 | 656 | | var content = await PutEntity(topicDescription.Path, atomRequest, false, null, null, cancellationToken).Conf |
| | | 657 | | |
| | 0 | 658 | | return TopicDescriptionExtensions.ParseFromContent(content); |
| | 0 | 659 | | } |
| | | 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 | | { |
| | 0 | 681 | | 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 | | { |
| | 0 | 702 | | subscriptionDescription = subscriptionDescription ?? throw new ArgumentNullException(nameof(subscriptionDesc |
| | 0 | 703 | | 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 | | { |
| | 0 | 723 | | subscriptionDescription = subscriptionDescription ?? throw new ArgumentNullException(nameof(subscriptionDesc |
| | 0 | 724 | | subscriptionDescription.NormalizeDescription(this.endpointFQDN); |
| | 0 | 725 | | subscriptionDescription.DefaultRuleDescription = defaultRule; |
| | 0 | 726 | | var atomRequest = subscriptionDescription.Serialize().ToString(); |
| | 0 | 727 | | var content = await PutEntity( |
| | 0 | 728 | | EntityNameHelper.FormatSubscriptionPath(subscriptionDescription.TopicPath, subscriptionDescription.Subsc |
| | 0 | 729 | | atomRequest, |
| | 0 | 730 | | false, |
| | 0 | 731 | | subscriptionDescription.ForwardTo, |
| | 0 | 732 | | subscriptionDescription.ForwardDeadLetteredMessagesTo, |
| | 0 | 733 | | cancellationToken).ConfigureAwait(false); |
| | 0 | 734 | | return SubscriptionDescriptionExtensions.ParseFromContent(subscriptionDescription.TopicPath, content); |
| | 0 | 735 | | } |
| | | 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 | | { |
| | 0 | 754 | | EntityNameHelper.CheckValidTopicName(topicPath); |
| | 0 | 755 | | EntityNameHelper.CheckValidSubscriptionName(subscriptionName); |
| | 0 | 756 | | ruleDescription = ruleDescription ?? throw new ArgumentNullException(nameof(ruleDescription)); |
| | | 757 | | |
| | 0 | 758 | | var atomRequest = ruleDescription.Serialize().ToString(); |
| | | 759 | | |
| | 0 | 760 | | var content = await PutEntity( |
| | 0 | 761 | | EntityNameHelper.FormatRulePath(topicPath, subscriptionName, ruleDescription.Name), |
| | 0 | 762 | | atomRequest, |
| | 0 | 763 | | false, |
| | 0 | 764 | | null, |
| | 0 | 765 | | null, |
| | 0 | 766 | | cancellationToken).ConfigureAwait(false); |
| | | 767 | | |
| | 0 | 768 | | return RuleDescriptionExtensions.ParseFromContent(content); |
| | 0 | 769 | | } |
| | | 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 | | { |
| | 0 | 789 | | queueDescription = queueDescription ?? throw new ArgumentNullException(nameof(queueDescription)); |
| | 0 | 790 | | queueDescription.NormalizeDescription(this.endpointFQDN); |
| | | 791 | | |
| | 0 | 792 | | var atomRequest = queueDescription.Serialize().ToString(); |
| | | 793 | | |
| | 0 | 794 | | var content = await PutEntity( |
| | 0 | 795 | | queueDescription.Path, |
| | 0 | 796 | | atomRequest, |
| | 0 | 797 | | true, |
| | 0 | 798 | | queueDescription.ForwardTo, |
| | 0 | 799 | | queueDescription.ForwardDeadLetteredMessagesTo, |
| | 0 | 800 | | cancellationToken).ConfigureAwait(false); |
| | | 801 | | |
| | 0 | 802 | | return QueueDescriptionExtensions.ParseFromContent(content); |
| | 0 | 803 | | } |
| | | 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 | | { |
| | 0 | 820 | | topicDescription = topicDescription ?? throw new ArgumentNullException(nameof(topicDescription)); |
| | 0 | 821 | | var atomRequest = topicDescription.Serialize().ToString(); |
| | | 822 | | |
| | 0 | 823 | | var content = await PutEntity(topicDescription.Path, atomRequest, true, null, null, cancellationToken).Confi |
| | | 824 | | |
| | 0 | 825 | | return TopicDescriptionExtensions.ParseFromContent(content); |
| | 0 | 826 | | } |
| | | 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 | | { |
| | 0 | 843 | | subscriptionDescription = subscriptionDescription ?? throw new ArgumentNullException(nameof(subscriptionDesc |
| | 0 | 844 | | subscriptionDescription.NormalizeDescription(this.endpointFQDN); |
| | 0 | 845 | | var atomRequest = subscriptionDescription.Serialize().ToString(); |
| | 0 | 846 | | var content = await PutEntity( |
| | 0 | 847 | | EntityNameHelper.FormatSubscriptionPath(subscriptionDescription.TopicPath, subscriptionDescription.Subsc |
| | 0 | 848 | | atomRequest, |
| | 0 | 849 | | true, |
| | 0 | 850 | | subscriptionDescription.ForwardTo, |
| | 0 | 851 | | subscriptionDescription.ForwardDeadLetteredMessagesTo, |
| | 0 | 852 | | cancellationToken).ConfigureAwait(false); |
| | 0 | 853 | | return SubscriptionDescriptionExtensions.ParseFromContent(subscriptionDescription.TopicPath, content); |
| | 0 | 854 | | } |
| | | 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 | | { |
| | 0 | 873 | | ruleDescription = ruleDescription ?? throw new ArgumentNullException(nameof(ruleDescription)); |
| | 0 | 874 | | EntityNameHelper.CheckValidTopicName(topicPath); |
| | 0 | 875 | | EntityNameHelper.CheckValidSubscriptionName(subscriptionName); |
| | | 876 | | |
| | 0 | 877 | | var atomRequest = ruleDescription.Serialize().ToString(); |
| | 0 | 878 | | var content = await PutEntity( |
| | 0 | 879 | | EntityNameHelper.FormatRulePath(topicPath, subscriptionName, ruleDescription.Name), |
| | 0 | 880 | | atomRequest, |
| | 0 | 881 | | true, |
| | 0 | 882 | | null, null, |
| | 0 | 883 | | cancellationToken).ConfigureAwait(false); |
| | | 884 | | |
| | 0 | 885 | | return RuleDescriptionExtensions.ParseFromContent(content); |
| | 0 | 886 | | } |
| | | 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 | | { |
| | 0 | 904 | | EntityNameHelper.CheckValidQueueName(queuePath); |
| | | 905 | | |
| | | 906 | | try |
| | | 907 | | { |
| | | 908 | | // TODO: Optimize by removing deserialization costs. |
| | 0 | 909 | | var qd = await GetQueueAsync(queuePath, cancellationToken).ConfigureAwait(false); |
| | 0 | 910 | | } |
| | 0 | 911 | | catch (MessagingEntityNotFoundException) |
| | | 912 | | { |
| | 0 | 913 | | return false; |
| | | 914 | | } |
| | | 915 | | |
| | 0 | 916 | | return true; |
| | 0 | 917 | | } |
| | | 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 | | { |
| | 0 | 932 | | EntityNameHelper.CheckValidTopicName(topicPath); |
| | | 933 | | |
| | | 934 | | try |
| | | 935 | | { |
| | | 936 | | // TODO: Optimize by removing deserialization costs. |
| | 0 | 937 | | var td = await GetTopicAsync(topicPath, cancellationToken).ConfigureAwait(false); |
| | 0 | 938 | | } |
| | 0 | 939 | | catch (MessagingEntityNotFoundException) |
| | | 940 | | { |
| | 0 | 941 | | return false; |
| | | 942 | | } |
| | | 943 | | |
| | 0 | 944 | | return true; |
| | 0 | 945 | | } |
| | | 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 | | { |
| | 0 | 961 | | EntityNameHelper.CheckValidTopicName(topicPath); |
| | 0 | 962 | | EntityNameHelper.CheckValidSubscriptionName(subscriptionName); |
| | | 963 | | |
| | | 964 | | try |
| | | 965 | | { |
| | | 966 | | // TODO: Optimize by removing deserialization costs. |
| | 0 | 967 | | var sd = await GetSubscriptionAsync(topicPath, subscriptionName, cancellationToken).ConfigureAwait(false |
| | 0 | 968 | | } |
| | 0 | 969 | | catch (MessagingEntityNotFoundException) |
| | | 970 | | { |
| | 0 | 971 | | return false; |
| | | 972 | | } |
| | | 973 | | |
| | 0 | 974 | | return true; |
| | 0 | 975 | | } |
| | | 976 | | |
| | | 977 | | public Task CloseAsync() |
| | | 978 | | { |
| | 0 | 979 | | httpClient?.Dispose(); |
| | 0 | 980 | | httpClient = null; |
| | | 981 | | |
| | 0 | 982 | | return Task.CompletedTask; |
| | | 983 | | } |
| | | 984 | | |
| | | 985 | | #endregion |
| | | 986 | | |
| | | 987 | | private static int GetPort(string endpoint) |
| | | 988 | | { |
| | | 989 | | // used for internal testing |
| | 6 | 990 | | if (endpoint.EndsWith("onebox.windows-int.net", StringComparison.InvariantCultureIgnoreCase)) |
| | | 991 | | { |
| | 0 | 992 | | return 4446; |
| | | 993 | | } |
| | | 994 | | |
| | 6 | 995 | | return -1; |
| | | 996 | | } |
| | | 997 | | |
| | | 998 | | private static async Task<Exception> ValidateHttpResponse(HttpResponseMessage response) |
| | | 999 | | { |
| | 0 | 1000 | | if (response.IsSuccessStatusCode) |
| | | 1001 | | { |
| | 0 | 1002 | | return null; |
| | | 1003 | | } |
| | | 1004 | | |
| | 0 | 1005 | | var exceptionMessage = await (response.Content?.ReadAsStringAsync() ?? Task.FromResult(string.Empty)); |
| | 0 | 1006 | | exceptionMessage = ParseDetailIfAvailable(exceptionMessage) ?? response.ReasonPhrase; |
| | | 1007 | | |
| | 0 | 1008 | | if (response.StatusCode == HttpStatusCode.Unauthorized) |
| | | 1009 | | { |
| | 0 | 1010 | | return new UnauthorizedException(exceptionMessage); |
| | | 1011 | | } |
| | | 1012 | | |
| | 0 | 1013 | | if (response.StatusCode == HttpStatusCode.NotFound || response.StatusCode == HttpStatusCode.NoContent) |
| | | 1014 | | { |
| | 0 | 1015 | | return new MessagingEntityNotFoundException(exceptionMessage); |
| | | 1016 | | } |
| | | 1017 | | |
| | 0 | 1018 | | if (response.StatusCode == HttpStatusCode.Conflict) |
| | | 1019 | | { |
| | 0 | 1020 | | if (response.RequestMessage.Method.Equals(HttpMethod.Delete)) |
| | | 1021 | | { |
| | 0 | 1022 | | return new ServiceBusException(true, exceptionMessage); |
| | | 1023 | | } |
| | | 1024 | | |
| | 0 | 1025 | | 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 |
| | 0 | 1028 | | return new ServiceBusException(true, exceptionMessage); |
| | | 1029 | | } |
| | | 1030 | | |
| | 0 | 1031 | | if (exceptionMessage.Contains(ManagementClientConstants.ConflictOperationInProgressSubCode)) |
| | | 1032 | | { |
| | 0 | 1033 | | return new ServiceBusException(true, exceptionMessage); |
| | | 1034 | | } |
| | | 1035 | | |
| | 0 | 1036 | | return new MessagingEntityAlreadyExistsException(exceptionMessage); |
| | | 1037 | | } |
| | | 1038 | | |
| | 0 | 1039 | | if (response.StatusCode == HttpStatusCode.Forbidden) |
| | | 1040 | | { |
| | 0 | 1041 | | if (exceptionMessage.Contains(ManagementClientConstants.ForbiddenInvalidOperationSubCode)) |
| | | 1042 | | { |
| | 0 | 1043 | | return new InvalidOperationException(exceptionMessage); |
| | | 1044 | | } |
| | | 1045 | | |
| | 0 | 1046 | | return new QuotaExceededException(exceptionMessage); |
| | | 1047 | | } |
| | | 1048 | | |
| | 0 | 1049 | | if (response.StatusCode == HttpStatusCode.BadRequest) |
| | | 1050 | | { |
| | 0 | 1051 | | return new ServiceBusException(false, new ArgumentException(exceptionMessage)); |
| | | 1052 | | } |
| | | 1053 | | |
| | 0 | 1054 | | if (response.StatusCode == HttpStatusCode.ServiceUnavailable) |
| | | 1055 | | { |
| | 0 | 1056 | | return new ServerBusyException(exceptionMessage); |
| | | 1057 | | } |
| | | 1058 | | |
| | 0 | 1059 | | return new ServiceBusException(true, exceptionMessage + "; response status code: " + response.StatusCode); |
| | 0 | 1060 | | } |
| | | 1061 | | |
| | | 1062 | | private static string ParseDetailIfAvailable(string content) |
| | | 1063 | | { |
| | 0 | 1064 | | if (string.IsNullOrWhiteSpace(content)) |
| | | 1065 | | { |
| | 0 | 1066 | | return null; |
| | | 1067 | | } |
| | | 1068 | | |
| | | 1069 | | try |
| | | 1070 | | { |
| | 0 | 1071 | | var errorContentXml = XElement.Parse(content); |
| | 0 | 1072 | | var detail = errorContentXml.Element("Detail"); |
| | | 1073 | | |
| | 0 | 1074 | | return detail?.Value ?? content; |
| | | 1075 | | } |
| | 0 | 1076 | | catch (Exception) |
| | | 1077 | | { |
| | 0 | 1078 | | return content; |
| | | 1079 | | } |
| | 0 | 1080 | | } |
| | | 1081 | | |
| | | 1082 | | private static ITokenProvider CreateTokenProvider(ServiceBusConnectionStringBuilder builder) |
| | | 1083 | | { |
| | 12 | 1084 | | if (builder.SasToken != null) |
| | | 1085 | | { |
| | 0 | 1086 | | return new SharedAccessSignatureTokenProvider(builder.SasToken); |
| | | 1087 | | } |
| | 12 | 1088 | | else if (builder.SasKeyName != null && builder.SasKey != null) |
| | | 1089 | | { |
| | 0 | 1090 | | return new SharedAccessSignatureTokenProvider(builder.SasKeyName, builder.SasKey); |
| | | 1091 | | } |
| | 12 | 1092 | | else if (builder.Authentication.Equals(ServiceBusConnectionStringBuilder.AuthenticationType.ManagedIdentity) |
| | | 1093 | | { |
| | 6 | 1094 | | return new ManagedIdentityTokenProvider(); |
| | | 1095 | | } |
| | | 1096 | | |
| | 6 | 1097 | | 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 | | { |
| | 0 | 1102 | | return this.GetToken(requestUri.GetLeftPart(UriPartial.Path)); |
| | | 1103 | | } |
| | | 1104 | | |
| | | 1105 | | private async Task<string> GetToken(string requestUri) |
| | | 1106 | | { |
| | 0 | 1107 | | var token = await this.tokenProvider.GetTokenAsync(requestUri, TimeSpan.FromHours(1)).ConfigureAwait(false); |
| | 0 | 1108 | | return token.TokenValue; |
| | 0 | 1109 | | } |
| | | 1110 | | |
| | | 1111 | | private async Task<string> GetEntity(string path, string query, bool enrich, CancellationToken cancellationToken |
| | | 1112 | | { |
| | 0 | 1113 | | MessagingEventSource.Log.ManagementOperationStart(this.clientId, nameof(GetEntity), $"path:{path},query:{que |
| | | 1114 | | |
| | 0 | 1115 | | var queryString = $"{ManagementClientConstants.apiVersionQuery}&enrich={enrich}"; |
| | 0 | 1116 | | if (query != null) |
| | | 1117 | | { |
| | 0 | 1118 | | queryString = queryString + "&" + query; |
| | | 1119 | | } |
| | 0 | 1120 | | var uri = new UriBuilder(this.endpointFQDN) |
| | 0 | 1121 | | { |
| | 0 | 1122 | | Path = path, |
| | 0 | 1123 | | Scheme = Uri.UriSchemeHttps, |
| | 0 | 1124 | | Port = this.port, |
| | 0 | 1125 | | Query = queryString |
| | 0 | 1126 | | }.Uri; |
| | | 1127 | | |
| | 0 | 1128 | | var request = new HttpRequestMessage(HttpMethod.Get, uri); |
| | 0 | 1129 | | HttpResponseMessage response = await SendHttpRequest(request, cancellationToken).ConfigureAwait(false); |
| | 0 | 1130 | | var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false); |
| | | 1131 | | |
| | 0 | 1132 | | MessagingEventSource.Log.ManagementOperationEnd(this.clientId, nameof(GetEntity), $"path:{path},query:{query |
| | 0 | 1133 | | return result; |
| | 0 | 1134 | | } |
| | | 1135 | | |
| | | 1136 | | private async Task<string> PutEntity(string path, string requestBody, bool isUpdate, string forwardTo, string fw |
| | | 1137 | | { |
| | 0 | 1138 | | MessagingEventSource.Log.ManagementOperationStart(this.clientId, nameof(PutEntity), $"path:{path},isUpdate:{ |
| | | 1139 | | |
| | 0 | 1140 | | var uri = new UriBuilder(this.endpointFQDN) |
| | 0 | 1141 | | { |
| | 0 | 1142 | | Path = path, |
| | 0 | 1143 | | Port = this.port, |
| | 0 | 1144 | | Scheme = Uri.UriSchemeHttps, |
| | 0 | 1145 | | Query = $"{ManagementClientConstants.apiVersionQuery}" |
| | 0 | 1146 | | }.Uri; |
| | | 1147 | | |
| | 0 | 1148 | | var request = new HttpRequestMessage(HttpMethod.Put, uri); |
| | 0 | 1149 | | request.Content = new StringContent( |
| | 0 | 1150 | | requestBody, |
| | 0 | 1151 | | Encoding.UTF8, |
| | 0 | 1152 | | ManagementClientConstants.AtomContentType |
| | 0 | 1153 | | ); |
| | | 1154 | | |
| | 0 | 1155 | | if (isUpdate) |
| | | 1156 | | { |
| | 0 | 1157 | | request.Headers.Add("If-Match", "*"); |
| | | 1158 | | } |
| | | 1159 | | |
| | 0 | 1160 | | if (!string.IsNullOrWhiteSpace(forwardTo)) |
| | | 1161 | | { |
| | 0 | 1162 | | var token = await this.GetToken(forwardTo).ConfigureAwait(false); |
| | 0 | 1163 | | request.Headers.Add(ManagementClientConstants.ServiceBusSupplementartyAuthorizationHeaderName, token); |
| | | 1164 | | } |
| | | 1165 | | |
| | 0 | 1166 | | if (!string.IsNullOrWhiteSpace(fwdDeadLetterTo)) |
| | | 1167 | | { |
| | 0 | 1168 | | var token = await this.GetToken(fwdDeadLetterTo).ConfigureAwait(false); |
| | 0 | 1169 | | request.Headers.Add(ManagementClientConstants.ServiceBusDlqSupplementaryAuthorizationHeaderName, token); |
| | | 1170 | | } |
| | | 1171 | | |
| | 0 | 1172 | | HttpResponseMessage response = await SendHttpRequest(request, cancellationToken).ConfigureAwait(false); |
| | 0 | 1173 | | var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false); |
| | | 1174 | | |
| | 0 | 1175 | | MessagingEventSource.Log.ManagementOperationEnd(this.clientId, nameof(PutEntity), $"path:{path},isUpdate:{is |
| | 0 | 1176 | | return result; |
| | 0 | 1177 | | } |
| | | 1178 | | |
| | | 1179 | | private async Task DeleteEntity(string path, CancellationToken cancellationToken) |
| | | 1180 | | { |
| | 0 | 1181 | | MessagingEventSource.Log.ManagementOperationStart(this.clientId, nameof(DeleteEntity), path); |
| | | 1182 | | |
| | 0 | 1183 | | var uri = new UriBuilder(this.endpointFQDN) |
| | 0 | 1184 | | { |
| | 0 | 1185 | | Path = path, |
| | 0 | 1186 | | Scheme = Uri.UriSchemeHttps, |
| | 0 | 1187 | | Port = this.port, |
| | 0 | 1188 | | Query = ManagementClientConstants.apiVersionQuery |
| | 0 | 1189 | | }.Uri; |
| | | 1190 | | |
| | 0 | 1191 | | var request = new HttpRequestMessage(HttpMethod.Delete, uri); |
| | 0 | 1192 | | await SendHttpRequest(request, cancellationToken).ConfigureAwait(false); |
| | 0 | 1193 | | MessagingEventSource.Log.ManagementOperationEnd(this.clientId, nameof(DeleteEntity), path); |
| | 0 | 1194 | | } |
| | | 1195 | | |
| | | 1196 | | private async Task<HttpResponseMessage> SendHttpRequest(HttpRequestMessage request, CancellationToken cancellati |
| | | 1197 | | { |
| | 0 | 1198 | | if (request.Headers.Authorization == null) |
| | | 1199 | | { |
| | | 1200 | | // First attempt. |
| | 0 | 1201 | | var token = await this.GetToken(request.RequestUri).ConfigureAwait(false); |
| | 0 | 1202 | | request.Headers.Add("Authorization", token); |
| | 0 | 1203 | | request.Headers.Add("UserAgent", $"SERVICEBUS/{ManagementClientConstants.ApiVersion}(api-origin={ClientI |
| | | 1204 | | } |
| | | 1205 | | else |
| | | 1206 | | { |
| | | 1207 | | // This is a retried request. |
| | 0 | 1208 | | request = CloneRequest(request); |
| | | 1209 | | } |
| | | 1210 | | |
| | | 1211 | | HttpResponseMessage response; |
| | | 1212 | | try |
| | | 1213 | | { |
| | 0 | 1214 | | response = await this.httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false); |
| | 0 | 1215 | | } |
| | 0 | 1216 | | catch (HttpRequestException exception) |
| | | 1217 | | { |
| | 0 | 1218 | | MessagingEventSource.Log.ManagementOperationException(this.clientId, nameof(SendHttpRequest), exception) |
| | 0 | 1219 | | throw new ServiceBusException(true, exception); |
| | | 1220 | | } |
| | | 1221 | | |
| | 0 | 1222 | | var exceptionReturned = await ValidateHttpResponse(response).ConfigureAwait(false); |
| | 0 | 1223 | | if (exceptionReturned == null) |
| | | 1224 | | { |
| | 0 | 1225 | | return response; |
| | | 1226 | | } |
| | | 1227 | | else |
| | | 1228 | | { |
| | 0 | 1229 | | MessagingEventSource.Log.ManagementOperationException(this.clientId, nameof(SendHttpRequest), exceptionR |
| | 0 | 1230 | | throw exceptionReturned; |
| | | 1231 | | } |
| | 0 | 1232 | | } |
| | | 1233 | | } |
| | | 1234 | | } |