diff --git a/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml b/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml index bf3ae5d9ef5e5..bf5ae0bed99ee 100755 --- a/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml +++ b/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml @@ -525,6 +525,7 @@ + diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationAsyncClient.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationAsyncClient.java index ec0991da6c9c7..c6575fc3f3789 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationAsyncClient.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationAsyncClient.java @@ -23,21 +23,26 @@ import com.azure.core.util.IterableStream; import com.azure.core.util.logging.ClientLogger; import com.azure.messaging.servicebus.administration.models.CreateQueueOptions; +import com.azure.messaging.servicebus.administration.models.CreateRuleOptions; import com.azure.messaging.servicebus.administration.models.CreateSubscriptionOptions; import com.azure.messaging.servicebus.administration.models.CreateTopicOptions; import com.azure.messaging.servicebus.administration.models.NamespaceProperties; import com.azure.messaging.servicebus.administration.models.QueueProperties; import com.azure.messaging.servicebus.administration.models.QueueRuntimeInfo; +import com.azure.messaging.servicebus.administration.models.RuleProperties; import com.azure.messaging.servicebus.administration.models.SubscriptionProperties; import com.azure.messaging.servicebus.administration.models.SubscriptionRuntimeInfo; import com.azure.messaging.servicebus.administration.models.TopicProperties; import com.azure.messaging.servicebus.administration.models.TopicRuntimeInfo; import com.azure.messaging.servicebus.implementation.EntitiesImpl; import com.azure.messaging.servicebus.implementation.EntityHelper; +import com.azure.messaging.servicebus.implementation.RulesImpl; import com.azure.messaging.servicebus.implementation.ServiceBusManagementClientImpl; import com.azure.messaging.servicebus.implementation.ServiceBusManagementSerializer; import com.azure.messaging.servicebus.implementation.models.CreateQueueBody; import com.azure.messaging.servicebus.implementation.models.CreateQueueBodyContent; +import com.azure.messaging.servicebus.implementation.models.CreateRuleBody; +import com.azure.messaging.servicebus.implementation.models.CreateRuleBodyContent; import com.azure.messaging.servicebus.implementation.models.CreateSubscriptionBody; import com.azure.messaging.servicebus.implementation.models.CreateSubscriptionBodyContent; import com.azure.messaging.servicebus.implementation.models.CreateTopicBody; @@ -47,6 +52,11 @@ import com.azure.messaging.servicebus.implementation.models.QueueDescriptionEntry; import com.azure.messaging.servicebus.implementation.models.QueueDescriptionFeed; import com.azure.messaging.servicebus.implementation.models.ResponseLink; +import com.azure.messaging.servicebus.implementation.models.RuleActionImpl; +import com.azure.messaging.servicebus.implementation.models.RuleDescription; +import com.azure.messaging.servicebus.implementation.models.RuleDescriptionEntry; +import com.azure.messaging.servicebus.implementation.models.RuleDescriptionFeed; +import com.azure.messaging.servicebus.implementation.models.RuleFilterImpl; import com.azure.messaging.servicebus.implementation.models.ServiceBusManagementError; import com.azure.messaging.servicebus.implementation.models.ServiceBusManagementErrorException; import com.azure.messaging.servicebus.implementation.models.SubscriptionDescription; @@ -102,6 +112,7 @@ public final class ServiceBusAdministrationAsyncClient { private final EntitiesImpl entityClient; private final ClientLogger logger = new ClientLogger(ServiceBusAdministrationAsyncClient.class); private final ServiceBusManagementSerializer serializer; + private final RulesImpl rulesClient; /** * Creates a new instance with the given management client and serializer. @@ -111,9 +122,10 @@ public final class ServiceBusAdministrationAsyncClient { */ ServiceBusAdministrationAsyncClient(ServiceBusManagementClientImpl managementClient, ServiceBusManagementSerializer serializer) { + this.serializer = Objects.requireNonNull(serializer, "'serializer' cannot be null."); this.managementClient = Objects.requireNonNull(managementClient, "'managementClient' cannot be null."); this.entityClient = managementClient.getEntities(); - this.serializer = serializer; + this.rulesClient = managementClient.getRules(); } /** @@ -182,6 +194,82 @@ public Mono> createQueueWithResponse(String queueName, return withContext(context -> createQueueWithResponse(queueName, queueOptions, context)); } + /** + * Creates a rule under the given topic and subscription + * + * @param topicName Name of the topic associated with rule. + * @param subscriptionName Name of the subscription associated with the rule. + * @param ruleName Name of the rule. + * + * @return A Mono that completes with information about the created rule. + * @throws ClientAuthenticationException if the client's credentials do not have access to modify the + * namespace. + * @throws HttpResponseException If the request body was invalid, the quota is exceeded, or an error occurred + * processing the request. + * @throws IllegalArgumentException if {@code topicName} or {@code ruleName} are are empty strings. + * @throws NullPointerException if {@code topicName} or {@code ruleName} are are null. + * @throws ResourceExistsException if a rule exists with the same topic, subscription, and rule name. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono createRule(String topicName, String subscriptionName, String ruleName) { + try { + return createRule(topicName, subscriptionName, ruleName, new CreateRuleOptions()); + } catch (RuntimeException e) { + return monoError(logger, e); + } + } + + /** + * Creates a rule with the {@link CreateRuleOptions}. + * + * @param topicName Name of the topic associated with rule. + * @param subscriptionName Name of the subscription associated with the rule. + * @param ruleName Name of the rule. + * @param ruleOptions Information about the rule to create. + * + * @return A Mono that completes with information about the created rule. + * @throws ClientAuthenticationException if the client's credentials do not have access to modify the + * namespace. + * @throws HttpResponseException If the request body was invalid, the quota is exceeded, or an error occurred + * processing the request. + * @throws IllegalArgumentException if {@code topicName} or {@code ruleName} are are empty strings. + * @throws NullPointerException if {@code topicName}, {@code ruleName}, or {@code ruleOptions} + * are are null. + * @throws ResourceExistsException if a rule exists with the same topic and rule name. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono createRule(String topicName, String subscriptionName, String ruleName, + CreateRuleOptions ruleOptions) { + + return createRuleWithResponse(topicName, subscriptionName, ruleName, ruleOptions) + .map(Response::getValue); + } + + /** + * Creates a rule and returns the created rule in addition to the HTTP response. + * + * @param topicName Name of the topic associated with rule. + * @param subscriptionName Name of the subscription associated with the rule. + * @param ruleName Name of the rule. + * @param ruleOptions Information about the rule to create. + * + * @return A Mono that returns the created rule in addition to the HTTP response. + * @throws ClientAuthenticationException if the client's credentials do not have access to modify the + * namespace. + * @throws HttpResponseException If the request body was invalid, the quota is exceeded, or an error occurred + * processing the request. + * @throws IllegalArgumentException if {@code topicName} or {@code ruleName} are are empty strings. + * @throws NullPointerException if {@code topicName}, {@code ruleName}, or {@code ruleOptions} + * are are null. + * @throws ResourceExistsException if a rule exists with the same topic and rule name. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> createRuleWithResponse(String topicName, String subscriptionName, + String ruleName, CreateRuleOptions ruleOptions) { + return withContext(context -> createRuleWithResponse(topicName, subscriptionName, ruleName, ruleOptions, + context)); + } + /** * Creates a subscription with the given topic and subscription names. * @@ -208,7 +296,7 @@ public Mono createSubscription(String topicName, String } /** - * Creates a subscription with the {@link SubscriptionProperties}. + * Creates a subscription with the {@link CreateSubscriptionOptions}. * * @param topicName Name of the topic associated with subscription. * @param subscriptionName Name of the subscription. @@ -228,17 +316,19 @@ public Mono createSubscription(String topicName, String @ServiceMethod(returns = ReturnType.SINGLE) public Mono createSubscription(String topicName, String subscriptionName, CreateSubscriptionOptions subscriptionOptions) { - return createSubscriptionWithResponse(topicName, subscriptionName, subscriptionOptions).map(Response::getValue); + + return createSubscriptionWithResponse(topicName, subscriptionName, subscriptionOptions) + .map(Response::getValue); } /** - * Creates a queue and returns the created queue in addition to the HTTP response. + * Creates a subscription and returns the created subscription in addition to the HTTP response. * * @param topicName Name of the topic associated with subscription. * @param subscriptionName Name of the subscription. * @param subscriptionOptions Information about the subscription to create. * - * @return A Mono that returns the created queue in addition to the HTTP response. + * @return A Mono that returns the created subscription in addition to the HTTP response. * @throws ClientAuthenticationException if the client's credentials do not have access to modify the * namespace. * @throws HttpResponseException If the request body was invalid, the quota is exceeded, or an error occurred @@ -361,6 +451,48 @@ public Mono> deleteQueueWithResponse(String queueName) { return withContext(context -> deleteQueueWithResponse(queueName, context)); } + /** + * Deletes a rule the matching {@code ruleName}. + * + * @param topicName Name of topic associated with rule to delete. + * @param subscriptionName Name of the subscription associated with the rule to delete. + * @param ruleName Name of rule to delete. + * + * @return A Mono that completes when the rule is deleted. + * @throws ClientAuthenticationException if the client's credentials do not have access to modify the + * namespace. + * @throws HttpResponseException If error occurred processing the request. + * @throws IllegalArgumentException if {@code topicName} or {@code ruleName} is an empty string. + * @throws NullPointerException if {@code topicName} or {@code ruleName} is null. + * @throws ResourceNotFoundException if the {@code ruleName} does not exist. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono deleteRule(String topicName, String subscriptionName, String ruleName) { + return deleteRuleWithResponse(topicName, subscriptionName, ruleName).then(); + } + + /** + * Deletes a rule the matching {@code ruleName} and returns the HTTP response. + * + * @param topicName Name of topic associated with rule to delete. + * @param subscriptionName Name of the subscription associated with the rule to delete. + * @param ruleName Name of rule to delete. + * + * @return A Mono that completes when the rule is deleted and returns the HTTP response. + * @throws ClientAuthenticationException if the client's credentials do not have access to modify the + * namespace. + * @throws HttpResponseException If error occurred processing the request. + * @throws IllegalArgumentException if {@code topicName}, {@code subscriptionName}, or {@code ruleName} is an + * empty string. + * @throws NullPointerException if {@code topicName}, {@code subscriptionName}, or {@code ruleName} is null. + * @throws ResourceNotFoundException if the {@code ruleName} does not exist. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> deleteRuleWithResponse(String topicName, String subscriptionName, + String ruleName) { + return withContext(context -> deleteRuleWithResponse(topicName, subscriptionName, ruleName, context)); + } + /** * Deletes a subscription the matching {@code subscriptionName}. * @@ -574,6 +706,41 @@ public Mono> getNamespacePropertiesWithResponse() return withContext(this::getNamespacePropertiesWithResponse); } + /** + * Gets a rule from the service namespace. + * + * Only following data types are deserialized in Filters and Action parameters - string, int, long, boolean, double, + * and OffsetDateTime. Other data types would return its string value. + * + * @param topicName The name of the topic relative to service bus namespace. + * @param subscriptionName The subscription name the rule belongs to. + * @param ruleName The name of the rule to retrieve. + * + * @return The associated rule. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono getRule(String topicName, String subscriptionName, String ruleName) { + return getRuleWithResponse(topicName, subscriptionName, ruleName).map(response -> response.getValue()); + } + + /** + * Gets a rule from the service namespace. + * + * Only following data types are deserialized in Filters and Action parameters - string, int, long, bool, double, + * and OffsetDateTime. Other data types would return its string value. + * + * @param topicName The name of the topic relative to service bus namespace. + * @param subscriptionName The subscription name the rule belongs to. + * @param ruleName The name of the rule to retrieve. + * + * @return The associated rule with the corresponding HTTP response. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> getRuleWithResponse(String topicName, String subscriptionName, + String ruleName) { + return withContext(context -> getRuleWithResponse(topicName, subscriptionName, ruleName, context)); + } + /** * Gets information about the queue. * @@ -822,6 +989,33 @@ public PagedFlux listQueues() { token -> withContext(context -> listQueuesNextPage(token, context))); } + /** + * Fetches all the rules for a topic and subscription. + * + * @param topicName The topic name under which all the rules need to be retrieved. + * @param subscriptionName The name of the subscription for which all rules need to be retrieved. + * + * @return A Flux of {@link RuleProperties rules} for the {@code topicName} and {@code subscriptionName}. + * @throws ClientAuthenticationException if the client's credentials do not have access to modify the + * namespace. + * @throws NullPointerException if {@code topicName} or {@code subscriptionName} is null. + * @throws IllegalArgumentException if {@code topicName} or {@code subscriptionName} is an empty string. + * @see List entities, rules, or + * authorization rules + */ + @ServiceMethod(returns = ReturnType.COLLECTION) + public PagedFlux listRules(String topicName, String subscriptionName) { + if (topicName == null) { + return pagedFluxError(logger, new NullPointerException("'topicName' cannot be null.")); + } else if (topicName.isEmpty()) { + return pagedFluxError(logger, new IllegalArgumentException("'topicName' cannot be an empty string.")); + } + + return new PagedFlux<>( + () -> withContext(context -> listRulesFirstPage(topicName, subscriptionName, context)), + token -> withContext(context -> listRulesNextPage(topicName, subscriptionName, token, context))); + } + /** * Fetches all the subscriptions for a topic. * @@ -940,6 +1134,67 @@ public Mono> updateQueueWithResponse(QueueProperties q return withContext(context -> updateQueueWithResponse(queue, context)); } + /** + * Updates a rule with the given {@link RuleProperties}. The {@link RuleProperties} must be fully populated as all + * of the properties are replaced. If a property is not set the service default value is used. + * + * The suggested flow is: + *
    + *
  1. {@link #getRule(String, String, String) Get rule description.}
  2. + *
  3. Update the required elements.
  4. + *
  5. Pass the updated description into this method.
  6. + *
+ * + * @param topicName The topic name under which the rule is updated. + * @param subscriptionName The name of the subscription for which the rule is updated. + * @param rule Information about the rule to update. You must provide all the property values that are desired + * on the updated entity. Any values not provided are set to the service default values. + * + * @return A Mono that returns the updated rule. + * @throws ClientAuthenticationException if the client's credentials do not have access to modify the + * namespace. + * @throws HttpResponseException If the request body was invalid, the rule quota is exceeded, or an error + * occurred processing the request. + * @throws IllegalArgumentException if {@link RuleProperties#getName()} is null or an empty string. + * @throws NullPointerException if {@code rule} is null. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono updateRule(String topicName, String subscriptionName, RuleProperties rule) { + return updateRuleWithResponse(topicName, subscriptionName, rule).map(Response::getValue); + } + + /** + * Updates a rule with the given {@link RuleProperties}. The {@link RuleProperties} must be fully populated as all + * of the properties are replaced. If a property is not set the service default value is used. + * + * The suggested flow is: + *
    + *
  1. {@link #getRule(String, String, String) Get rule description.}
  2. + *
  3. Update the required elements.
  4. + *
  5. Pass the updated description into this method.
  6. + *
+ * + * @param topicName The topic name under which the rule is updated. + * @param subscriptionName The name of the subscription for which the rule is updated. + * @param rule Information about the rule to update. You must provide all the property values that are desired + * on the updated entity. Any values not provided are set to the service default values. + * + * @return A Mono that returns the updated rule in addition to the HTTP response. + * @throws ClientAuthenticationException if the client's credentials do not have access to modify the + * namespace. + * @throws HttpResponseException If the request body was invalid, the rule quota is exceeded, or an error + * occurred processing the request. + * @throws IllegalArgumentException if {@link RuleProperties#getName()} is null or an empty string. + * @throws NullPointerException if {@code rule} is null. + * @see Create or Update Entity + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> updateRuleWithResponse(String topicName, String subscriptionName, + RuleProperties rule) { + + return withContext(context -> updateRuleWithResponse(topicName, subscriptionName, rule, context)); + } + /** * Updates a subscription with the given {@link SubscriptionProperties}. The {@link SubscriptionProperties} must be * fully populated as all of the properties are replaced. If a property is not set the service default value is @@ -1134,17 +1389,76 @@ Mono> createQueueWithResponse(String queueName, Create } } + /** + * Creates a rule with its context. + * + * @param ruleOptions Rule to create. + * @param context Context to pass into request. + * + * @return A Mono that completes with the created {@link RuleProperties}. + */ + Mono> createRuleWithResponse(String topicName, String subscriptionName, String ruleName, + CreateRuleOptions ruleOptions, Context context) { + if (topicName == null) { + return monoError(logger, new NullPointerException("'topicName' cannot be null.")); + } else if (topicName.isEmpty()) { + return monoError(logger, new IllegalArgumentException("'topicName' cannot be empty.")); + } + + if (subscriptionName == null) { + return monoError(logger, new NullPointerException("'subscriptionName' cannot be null.")); + } else if (subscriptionName.isEmpty()) { + return monoError(logger, new IllegalArgumentException("'subscriptionName' cannot be empty.")); + } + + if (ruleName == null) { + return monoError(logger, new NullPointerException("'ruleName' cannot be null.")); + } else if (ruleName.isEmpty()) { + return monoError(logger, new IllegalArgumentException("'ruleName' cannot be empty.")); + } + + if (ruleOptions == null) { + return monoError(logger, new NullPointerException("'rule' cannot be null.")); + } + + final RuleActionImpl action = ruleOptions.getAction() != null + ? EntityHelper.toImplementation(ruleOptions.getAction()) + : null; + final RuleFilterImpl filter = ruleOptions.getFilter() != null + ? EntityHelper.toImplementation(ruleOptions.getFilter()) + : null; + final RuleDescription rule = new RuleDescription() + .setAction(action) + .setFilter(filter) + .setName(ruleName); + + final CreateRuleBodyContent content = new CreateRuleBodyContent() + .setType(CONTENT_TYPE) + .setRuleDescription(rule); + final CreateRuleBody createEntity = new CreateRuleBody().setContent(content); + + final Context withTracing = context.addData(AZ_TRACING_NAMESPACE_KEY, SERVICE_BUS_TRACING_NAMESPACE_VALUE); + + try { + return managementClient.getRules().putWithResponseAsync(topicName, subscriptionName, ruleName, createEntity, + null, withTracing) + .onErrorMap(ServiceBusAdministrationAsyncClient::mapException) + .map(response -> deserializeRule(response)); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + /** * Creates a subscription with its context. * - * @param options Subscription to create. + * @param subscriptionOptions Subscription to create. * @param context Context to pass into request. * * @return A Mono that completes with the created {@link SubscriptionProperties}. */ Mono> createSubscriptionWithResponse(String topicName, String subscriptionName, - CreateSubscriptionOptions options, - Context context) { + CreateSubscriptionOptions subscriptionOptions, Context context) { if (topicName == null) { return monoError(logger, new NullPointerException("'topicName' cannot be null.")); } else if (topicName.isEmpty()) { @@ -1157,11 +1471,11 @@ Mono> createSubscriptionWithResponse(String top return monoError(logger, new IllegalArgumentException("'subscriptionName' cannot be empty.")); } - if (options == null) { + if (subscriptionOptions == null) { return monoError(logger, new NullPointerException("'subscription' cannot be null.")); } - final SubscriptionDescription subscription = EntityHelper.getSubscriptionDescription(options); + final SubscriptionDescription subscription = EntityHelper.getSubscriptionDescription(subscriptionOptions); final CreateSubscriptionBodyContent content = new CreateSubscriptionBodyContent() .setType(CONTENT_TYPE) .setSubscriptionDescription(subscription); @@ -1225,7 +1539,7 @@ Mono> createTopicWithResponse(String topicName, Create * @param queueName Name of queue to delete. * @param context Context to pass into request. * - * @return A Mono that completes with the created {@link QueueProperties}. + * @return A Mono that completes when the queue is deleted. */ Mono> deleteQueueWithResponse(String queueName, Context context) { if (queueName == null) { @@ -1250,6 +1564,46 @@ Mono> deleteQueueWithResponse(String queueName, Context context) } } + /** + * Deletes a queue with its context. + * + * @param topicName Name of topic to delete. + * @param subscriptionName Name of the subscription for the rule. + * @param ruleName Name of the rule. + * @param context Context to pass into request. + * + * @return A Mono that completes with the created {@link QueueProperties}. + */ + Mono> deleteRuleWithResponse(String topicName, String subscriptionName, String ruleName, + Context context) { + if (topicName == null) { + return monoError(logger, new NullPointerException("'topicName' cannot be null")); + } else if (topicName.isEmpty()) { + return monoError(logger, new IllegalArgumentException("'topicName' cannot be an empty string.")); + } else if (subscriptionName == null) { + return monoError(logger, new NullPointerException("'subscriptionName' cannot be null")); + } else if (subscriptionName.isEmpty()) { + return monoError(logger, new IllegalArgumentException("'subscriptionName' cannot be an empty string.")); + } else if (ruleName == null) { + return monoError(logger, new NullPointerException("'ruleName' cannot be null")); + } else if (ruleName.isEmpty()) { + return monoError(logger, new IllegalArgumentException("'ruleName' cannot be an empty string.")); + } else if (context == null) { + return monoError(logger, new NullPointerException("'context' cannot be null.")); + } + + final Context withTracing = context.addData(AZ_TRACING_NAMESPACE_KEY, SERVICE_BUS_TRACING_NAMESPACE_VALUE); + + try { + return rulesClient.deleteWithResponseAsync(topicName, subscriptionName, ruleName, withTracing) + .onErrorMap(ServiceBusAdministrationAsyncClient::mapException) + .map(response -> new SimpleResponse<>(response.getRequest(), response.getStatusCode(), + response.getHeaders(), null)); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + /** * Deletes a subscription with its context. * @@ -1381,6 +1735,19 @@ Mono> getQueueWithResponse(String queueName, Context context, } } + Mono> getRuleWithResponse(String topicName, String subscriptionName, + String ruleName, Context context) { + final Context withTracing = context.addData(AZ_TRACING_NAMESPACE_KEY, SERVICE_BUS_TRACING_NAMESPACE_VALUE); + + try { + return rulesClient.getWithResponseAsync(topicName, subscriptionName, ruleName, true, withTracing) + .onErrorMap(ServiceBusAdministrationAsyncClient::mapException) + .map(this::deserializeRule); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + /** * Gets a subscription with its context. * @@ -1536,6 +1903,47 @@ Mono> listQueuesNextPage(String continuationToken } } + /** + * Gets the first page of rules with context. + * + * @param context Context to pass into request. + * + * @return A Mono that completes with a page of rules. + */ + Mono> listRulesFirstPage(String topicName, String subscriptionName, Context context) { + final Context withTracing = context.addData(AZ_TRACING_NAMESPACE_KEY, SERVICE_BUS_TRACING_NAMESPACE_VALUE); + + try { + return listRules(topicName, subscriptionName, 0, withTracing); + } catch (RuntimeException e) { + return monoError(logger, e); + } + } + + /** + * Gets the next page of rules with context. + * + * @param continuationToken Number of items to skip in feed. + * @param context Context to pass into request. + * + * @return A Mono that completes with a page of rules or empty if there are no items left. + */ + Mono> listRulesNextPage(String topicName, String subscriptionName, + String continuationToken, Context context) { + if (continuationToken == null || continuationToken.isEmpty()) { + return Mono.empty(); + } + + try { + final Context withTracing = context.addData(AZ_TRACING_NAMESPACE_KEY, SERVICE_BUS_TRACING_NAMESPACE_VALUE); + final int skip = Integer.parseInt(continuationToken); + + return listRules(topicName, subscriptionName, skip, withTracing); + } catch (RuntimeException e) { + return monoError(logger, e); + } + } + /** * Gets the first page of subscriptions with context. * @@ -1651,6 +2059,42 @@ Mono> updateQueueWithResponse(QueueProperties queue, C } } + /** + * Updates a rule with its context. + * + * @param rule Information about the rule to update. You must provide all the property values that are desired + * on the updated entity. Any values not provided are set to the service default values. + * @param context Context to pass into request. + * + * @return A Mono that completes with the updated {@link RuleProperties}. + */ + Mono> updateRuleWithResponse(String topicName, String subscriptionName, + RuleProperties rule, Context context) { + if (rule == null) { + return monoError(logger, new NullPointerException("'rule' cannot be null")); + } else if (context == null) { + return monoError(logger, new NullPointerException("'context' cannot be null.")); + } + + final RuleDescription implementation = EntityHelper.toImplementation(rule); + final CreateRuleBodyContent content = new CreateRuleBodyContent() + .setType(CONTENT_TYPE) + .setRuleDescription(implementation); + final CreateRuleBody ruleBody = new CreateRuleBody() + .setContent(content); + final Context withTracing = context.addData(AZ_TRACING_NAMESPACE_KEY, SERVICE_BUS_TRACING_NAMESPACE_VALUE); + + try { + // If-Match == "*" to unconditionally update. This is in line with the existing client library behaviour. + return managementClient.getRules().putWithResponseAsync(topicName, subscriptionName, rule.getName(), + ruleBody, "*", withTracing) + .onErrorMap(ServiceBusAdministrationAsyncClient::mapException) + .map(response -> deserializeRule(response)); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + /** * Updates a subscription with its context. * @@ -1783,6 +2227,30 @@ private Response deserializeQueue(Response response) { return new SimpleResponse<>(response.getRequest(), response.getStatusCode(), response.getHeaders(), result); } + /** + * Converts a Response into its corresponding {@link RuleDescriptionEntry} then mapped into {@link RuleProperties}. + * + * @param response HTTP Response to deserialize. + * + * @return The corresponding HTTP response with convenience properties set. + */ + private Response deserializeRule(Response response) { + final RuleDescriptionEntry entry = deserialize(response.getValue(), RuleDescriptionEntry.class); + + // This was an empty response (ie. 204). + if (entry == null) { + return new SimpleResponse<>(response.getRequest(), response.getStatusCode(), response.getHeaders(), null); + } else if (entry.getContent() == null) { + logger.info("entry.getContent() is null. The entity may not exist. {}", entry); + return new SimpleResponse<>(response.getRequest(), response.getStatusCode(), response.getHeaders(), null); + } + + final RuleDescription description = entry.getContent().getRuleDescription(); + final RuleProperties result = EntityHelper.toModel(description); + + return new SimpleResponse<>(response.getRequest(), response.getStatusCode(), response.getHeaders(), result); + } + /** * Converts a Response into its corresponding {@link SubscriptionDescriptionEntry} then mapped into {@link * SubscriptionProperties}. @@ -1917,6 +2385,46 @@ private Mono> listQueues(int skip, Context contex }); } + + /** + * Helper method that invokes the service method, extracts the data and translates it to a PagedResponse. + * + * @param skip Number of elements to skip. + * @param context Context for the query. + * + * @return A Mono that completes with a paged response of rules. + */ + private Mono> listRules(String topicName, String subscriptionName, int skip, + Context context) { + return managementClient.listRulesWithResponseAsync(topicName, subscriptionName, skip, NUMBER_OF_ELEMENTS, + context) + .onErrorMap(ServiceBusAdministrationAsyncClient::mapException) + .flatMap(response -> { + final Response feedResponse = deserialize(response, + RuleDescriptionFeed.class); + + final RuleDescriptionFeed feed = feedResponse.getValue(); + if (feed == null) { + logger.warning("Could not deserialize RuleDescriptionFeed. skip {}, top: {}", skip, + NUMBER_OF_ELEMENTS); + return Mono.empty(); + } + + final List entities = feed.getEntry().stream() + .filter(e -> e.getContent() != null && e.getContent().getRuleDescription() != null) + .map(e -> { + return EntityHelper.toModel(e.getContent().getRuleDescription()); + }) + .collect(Collectors.toList()); + try { + return Mono.just(extractPage(feedResponse, entities, feed.getLink())); + } catch (MalformedURLException | UnsupportedEncodingException error) { + return Mono.error(new RuntimeException( + "Could not parse response into FeedPage", error)); + } + }); + } + /** * Helper method that invokes the service method, extracts the data and translates it to a PagedResponse. * diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationClient.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationClient.java index d0334be730f04..1af995b296b85 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationClient.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationClient.java @@ -15,11 +15,13 @@ import com.azure.core.http.rest.Response; import com.azure.core.util.Context; import com.azure.messaging.servicebus.administration.models.CreateQueueOptions; +import com.azure.messaging.servicebus.administration.models.CreateRuleOptions; import com.azure.messaging.servicebus.administration.models.CreateSubscriptionOptions; import com.azure.messaging.servicebus.administration.models.CreateTopicOptions; import com.azure.messaging.servicebus.administration.models.NamespaceProperties; import com.azure.messaging.servicebus.administration.models.QueueProperties; import com.azure.messaging.servicebus.administration.models.QueueRuntimeInfo; +import com.azure.messaging.servicebus.administration.models.RuleProperties; import com.azure.messaging.servicebus.administration.models.SubscriptionProperties; import com.azure.messaging.servicebus.administration.models.SubscriptionRuntimeInfo; import com.azure.messaging.servicebus.administration.models.TopicProperties; @@ -80,8 +82,7 @@ public QueueProperties createQueue(String queueName) { * @throws HttpResponseException If the request body was invalid, the queue quota is exceeded, or an error * occurred processing the request. * @throws NullPointerException if {@code queue} is null. - * @throws ResourceExistsException if a queue exists with the same {@link QueueProperties#getName() - * queueName}. + * @throws ResourceExistsException if a queue exists with the same {@link QueueProperties#getName() queueName}. * @see Create or Update Entity */ @ServiceMethod(returns = ReturnType.SINGLE) @@ -102,8 +103,7 @@ public QueueProperties createQueue(String queueName, CreateQueueOptions queueOpt * @throws HttpResponseException If the request body was invalid, the queue quota is exceeded, or an error * occurred processing the request. * @throws NullPointerException if {@code queue} is null. - * @throws ResourceExistsException if a queue exists with the same {@link QueueProperties#getName() - * queueName}. + * @throws ResourceExistsException if a queue exists with the same {@link QueueProperties#getName() queueName}. * @see Create or Update Entity */ @ServiceMethod(returns = ReturnType.SINGLE) @@ -113,6 +113,74 @@ public Response createQueueWithResponse(String queueName, Creat .block(); } + /** + * Creates a rule under the given topic and subscription + * + * @param topicName Name of the topic associated with rule. + * @param subscriptionName Name of the subscription associated with the rule. + * @param ruleName Name of the rule. + * + * @return Information about the created rule. + * @throws ClientAuthenticationException if the client's credentials do not have access to modify the + * namespace. + * @throws HttpResponseException If the request body was invalid, the quota is exceeded, or an error occurred + * processing the request. + * @throws IllegalArgumentException if {@code topicName} or {@code ruleName} are are empty strings. + * @throws NullPointerException if {@code topicName} or {@code ruleName} are are null. + * @throws ResourceExistsException if a rule exists with the same topic, subscription, and rule name. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public RuleProperties createRule(String topicName, String subscriptionName, String ruleName) { + return asyncClient.createRule(topicName, subscriptionName, ruleName).block(); + } + + /** + * Creates a rule with the {@link CreateRuleOptions}. + * + * @param topicName Name of the topic associated with rule. + * @param subscriptionName Name of the subscription associated with the rule. + * @param ruleName Name of the rule. + * @param ruleOptions Information about the rule to create. + * + * @return Information about the created rule. + * @throws ClientAuthenticationException if the client's credentials do not have access to modify the + * namespace. + * @throws HttpResponseException If the request body was invalid, the quota is exceeded, or an error occurred + * processing the request. + * @throws IllegalArgumentException if {@code topicName} or {@code ruleName} are are empty strings. + * @throws NullPointerException if {@code topicName}, {@code ruleName}, or {@code ruleOptions} are are null. + * @throws ResourceExistsException if a rule exists with the same topic and rule name. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public RuleProperties createRule(String topicName, String ruleName, String subscriptionName, + CreateRuleOptions ruleOptions) { + return asyncClient.createRule(topicName, subscriptionName, ruleName, ruleOptions).block(); + } + + /** + * Creates a rule and returns the created rule in addition to the HTTP response. + * + * @param topicName Name of the topic associated with rule. + * @param subscriptionName Name of the subscription associated with the rule. + * @param ruleName Name of the rule. + * @param ruleOptions Information about the rule to create. + * @param context Additional context that is passed through the HTTP pipeline during the service call. + * + * @return The created rule in addition to the HTTP response. + * @throws ClientAuthenticationException if the client's credentials do not have access to modify the + * namespace. + * @throws HttpResponseException If the request body was invalid, the quota is exceeded, or an error occurred + * processing the request. + * @throws IllegalArgumentException if {@code topicName} or {@code ruleName} are are empty strings. + * @throws NullPointerException if {@code topicName}, {@code ruleName}, or {@code ruleOptions} are are null. + * @throws ResourceExistsException if a rule exists with the same topic and rule name. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response createRuleWithResponse(String topicName, String subscriptionName, + String ruleName, CreateRuleOptions ruleOptions, Context context) { + return asyncClient.createRuleWithResponse(topicName, subscriptionName, ruleName, ruleOptions, context).block(); + } + /** * Creates a subscription with the given topic and subscription names. * @@ -166,7 +234,7 @@ public SubscriptionProperties createSubscription(String topicName, String subscr * @param subscriptionOptions Information about the subscription to create. * @param context Additional context that is passed through the HTTP pipeline during the service call. * - * @return The created queue in addition to the HTTP response. + * @return The created subscription in addition to the HTTP response. * @throws ClientAuthenticationException if the client's credentials do not have access to modify the * namespace. * @throws HttpResponseException If the request body was invalid, the quota is exceeded, or an error occurred @@ -285,6 +353,49 @@ public Response deleteQueueWithResponse(String queueName, Context context) return asyncClient.deleteQueueWithResponse(queueName, context != null ? context : Context.NONE).block(); } + /** + * Deletes a rule the matching {@code ruleName}. + * + * @param topicName Name of topic associated with rule to delete. + * @param subscriptionName Name of the subscription associated with the rule to delete. + * @param ruleName Name of rule to delete. + * + * @throws ClientAuthenticationException if the client's credentials do not have access to modify the + * namespace. + * @throws HttpResponseException If error occurred processing the request. + * @throws IllegalArgumentException if {@code topicName} or {@code ruleName} is an empty string. + * @throws NullPointerException if {@code topicName} or {@code ruleName} is null. + * @throws ResourceNotFoundException if the {@code ruleName} does not exist. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public void deleteRule(String topicName, String subscriptionName, String ruleName) { + asyncClient.deleteRule(topicName, subscriptionName, ruleName).block(); + } + + /** + * Deletes a rule the matching {@code ruleName} and returns the HTTP response. + * + * @param topicName Name of topic associated with rule to delete. + * @param subscriptionName Name of the subscription associated with the rule to delete. + * @param ruleName Name of rule to delete. + * @param context Additional context that is passed through the HTTP pipeline during the service call. + * + * @return The HTTP response. + * @throws ClientAuthenticationException if the client's credentials do not have access to modify the + * namespace. + * @throws HttpResponseException If error occurred processing the request. + * @throws IllegalArgumentException if {@code topicName}, {@code subscriptionName}, or {@code ruleName} is an + * empty string. + * @throws NullPointerException if {@code topicName}, {@code subscriptionName}, or {@code ruleName} is null. + * @throws ResourceNotFoundException if the {@code ruleName} does not exist. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response deleteRuleWithResponse(String topicName, String subscriptionName, + String ruleName, Context context) { + return asyncClient.deleteRuleWithResponse(topicName, subscriptionName, ruleName, + context != null ? context : Context.NONE).block(); + } + /** * Deletes a subscription the matching {@code subscriptionName}. * @@ -485,7 +596,7 @@ public Response getQueueRuntimeInfoWithResponse(String queueNa /** * Gets information about the Service Bus namespace. * - * @return A Mono that completes with information about the Service Bus namespace. + * @return Information about the Service Bus namespace. * @throws ClientAuthenticationException if the client's credentials do not have access to the namespace. * @throws HttpResponseException If error occurred processing the request. */ @@ -499,7 +610,7 @@ public NamespaceProperties getNamespaceProperties() { * * @param context Additional context that is passed through the HTTP pipeline during the service call. * - * @return A Mono that completes with information about the namespace and the associated HTTP response. + * @return Information about the namespace and the associated HTTP response. * @throws ClientAuthenticationException if the client's credentials do not have access to modify the * namespace. * @throws HttpResponseException If error occurred processing the request. @@ -509,6 +620,43 @@ public Response getNamespacePropertiesWithResponse(Context return asyncClient.getNamespacePropertiesWithResponse(context).block(); } + /** + * Gets a rule from the service namespace. + * + * Only following data types are deserialized in Filters and Action parameters - string, int, long, boolean, double, + * and OffsetDateTime. Other data types would return its string value. + * + * @param topicName The name of the topic relative to service bus namespace. + * @param subscriptionName The subscription name the rule belongs to. + * @param ruleName The name of the rule to retrieve. + * + * @return The associated rule. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public RuleProperties getRule(String topicName, String subscriptionName, String ruleName) { + return asyncClient.getRule(topicName, subscriptionName, ruleName).block(); + } + + /** + * Gets a rule from the service namespace. + * + * Only following data types are deserialized in Filters and Action parameters - string, int, long, bool, double, + * and OffsetDateTime. Other data types would return its string value. + * + * @param topicName The name of the topic relative to service bus namespace. + * @param subscriptionName The subscription name the rule belongs to. + * @param ruleName The name of the rule to retrieve. + * @param context Additional context that is passed through the HTTP pipeline during the service call. + * + * @return The associated rule with the corresponding HTTP response. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response getRuleWithResponse(String topicName, String subscriptionName, + String ruleName, Context context) { + return asyncClient.getRuleWithResponse(topicName, subscriptionName, ruleName, + context != null ? context : Context.NONE).block(); + } + /** * Gets information about the queue. * @@ -790,6 +938,25 @@ public PagedIterable listQueues(Context context) { return new PagedIterable<>(pagedFlux); } + /** + * Fetches all the rules for a topic and subscription. + * + * @param topicName The topic name under which all the rules need to be retrieved. + * @param subscriptionName The name of the subscription for which all rules need to be retrieved. + * + * @return An iterable of {@link RuleProperties rules} for the {@code topicName} and {@code subscriptionName}. + * @throws ClientAuthenticationException if the client's credentials do not have access to modify the + * namespace. + * @throws NullPointerException if {@code topicName} or {@code subscriptionName} is null. + * @throws IllegalArgumentException if {@code topicName} or {@code subscriptionName} is an empty string. + * @see List entities, rules, or + * authorization rules + */ + @ServiceMethod(returns = ReturnType.COLLECTION) + public PagedIterable listRules(String topicName, String subscriptionName) { + return new PagedIterable<>(asyncClient.listRules(topicName, subscriptionName)); + } + /** * Fetches all the subscriptions for a topic. * @@ -945,8 +1112,70 @@ public Response updateQueueWithResponse(QueueProperties queue, } /** - * Updates a subscription with the given {@link SubscriptionProperties}. The {@link SubscriptionProperties} must - * be fully populated as all of the properties are replaced. If a property is not set the service default value is + * Updates a rule with the given {@link RuleProperties}. The {@link RuleProperties} must be fully populated as all + * of the properties are replaced. If a property is not set the service default value is used. + * + * The suggested flow is: + *
    + *
  1. {@link #getRule(String, String, String) Get rule description.}
  2. + *
  3. Update the required elements.
  4. + *
  5. Pass the updated description into this method.
  6. + *
+ * + * @param topicName The topic name under which the rule is updated. + * @param subscriptionName The name of the subscription for which the rule is updated. + * @param rule Information about the rule to update. You must provide all the property values that are desired + * on the updated entity. Any values not provided are set to the service default values. + * + * @return The updated rule. + * @throws ClientAuthenticationException if the client's credentials do not have access to modify the + * namespace. + * @throws HttpResponseException If the request body was invalid, the rule quota is exceeded, or an error + * occurred processing the request. + * @throws IllegalArgumentException if {@link RuleProperties#getName()} is null or an empty string. + * @throws NullPointerException if {@code rule} is null. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public RuleProperties updateRule(String topicName, String subscriptionName, RuleProperties rule) { + return asyncClient.updateRule(topicName, subscriptionName, rule).block(); + } + + /** + * Updates a rule with the given {@link RuleProperties}. The {@link RuleProperties} must be fully populated as all + * of the properties are replaced. If a property is not set the service default value is used. + * + * The suggested flow is: + *
    + *
  1. {@link #getRule(String, String, String) Get rule description.}
  2. + *
  3. Update the required elements.
  4. + *
  5. Pass the updated description into this method.
  6. + *
+ * + * @param topicName The topic name under which the rule is updated. + * @param subscriptionName The name of the subscription for which the rule is updated. + * @param rule Information about the rule to update. You must provide all the property values that are desired + * on the updated entity. Any values not provided are set to the service default values. + * @param context Additional context that is passed through the HTTP pipeline during the service call. + * + * @return A Mono that returns the updated rule in addition to the HTTP response. + * @throws ClientAuthenticationException if the client's credentials do not have access to modify the + * namespace. + * @throws HttpResponseException If the request body was invalid, the rule quota is exceeded, or an error + * occurred processing the request. + * @throws IllegalArgumentException if {@link RuleProperties#getName()} is null or an empty string. + * @throws NullPointerException if {@code rule} is null. + * @see Create or Update Entity + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response updateRuleWithResponse(String topicName, String subscriptionName, + RuleProperties rule, Context context) { + return asyncClient.updateRuleWithResponse(topicName, subscriptionName, rule, + context != null ? context : Context.NONE).block(); + } + + /** + * Updates a subscription with the given {@link SubscriptionProperties}. The {@link SubscriptionProperties} must be + * fully populated as all of the properties are replaced. If a property is not set the service default value is * used. * * The suggested flow is: @@ -967,7 +1196,7 @@ public Response updateQueueWithResponse(QueueProperties queue, * @param subscription Information about the subscription to update. You must provide all the property values * that are desired on the updated entity. Any values not provided are set to the service default values. * - * @return A Mono that returns the updated subscription in addition to the HTTP response. + * @return Updated subscription in addition to the HTTP response. * @throws ClientAuthenticationException if the client's credentials do not have access to modify the * namespace. * @throws HttpResponseException If the request body was invalid, the subscription quota is exceeded, or an @@ -983,8 +1212,8 @@ public SubscriptionProperties updateSubscription(SubscriptionProperties subscrip } /** - * Updates a subscription with the given {@link SubscriptionProperties}. The {@link SubscriptionProperties} must - * be fully populated as all of the properties are replaced. If a property is not set the service default value is + * Updates a subscription with the given {@link SubscriptionProperties}. The {@link SubscriptionProperties} must be + * fully populated as all of the properties are replaced. If a property is not set the service default value is * used. * * The suggested flow is: @@ -1006,7 +1235,7 @@ public SubscriptionProperties updateSubscription(SubscriptionProperties subscrip * that are desired on the updated entity. Any values not provided are set to the service default values. * @param context Additional context that is passed through the HTTP pipeline during the service call. * - * @return A Mono that returns the updated subscription in addition to the HTTP response. + * @return Updated subscription in addition to the HTTP response. * @throws ClientAuthenticationException if the client's credentials do not have access to modify the * namespace. * @throws HttpResponseException If the request body was invalid, the subscription quota is exceeded, or an diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/CorrelationRuleFilter.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/CorrelationRuleFilter.java new file mode 100644 index 0000000000000..da66d912e9add --- /dev/null +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/CorrelationRuleFilter.java @@ -0,0 +1,294 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.messaging.servicebus.administration.models; + +import com.azure.core.annotation.Fluent; +import com.azure.core.util.logging.ClientLogger; +import com.azure.messaging.servicebus.ServiceBusMessage; + +import java.util.HashMap; +import java.util.Map; + +/** + * Represents the correlation rule filter expression. + *

+ * A CorrelationRuleFilter holds a set of conditions that are matched against one of more of an arriving message's user + * and system properties. A common use is a match against the {@link ServiceBusMessage#getCorrelationId()} property, but + * the application can also choose to match against {@link ServiceBusMessage#getContentType()}, {@link + * ServiceBusMessage#getLabel()}, {@link ServiceBusMessage#getMessageId()}, {@link ServiceBusMessage#getReplyTo()}, + * {@link ServiceBusMessage#getReplyToSessionId()}, {@link ServiceBusMessage#getSessionId()}, {@link + * ServiceBusMessage#getTo()}, and any user-defined properties. A match exists when an arriving message's value for a + * property is equal to the value specified in the correlation filter. For string expressions, the comparison is + * case-sensitive. When specifying multiple match properties, the filter combines them as a logical AND condition, + * meaning all conditions must match for the filter to match. + *

+ *

+ * The CorrelationRuleFilter provides an efficient shortcut for declarations of filters that deal only with correlation + * equality. In this case the cost of the lexigraphical analysis of the expression can be avoided. Not only will + * correlation filters be optimized at declaration time, but they will also be optimized at runtime. Correlation filter + * matching can be reduced to a hashtable lookup, which aggregates the complexity of the set of defined correlation + * filters to O(1). + *

+ */ +@Fluent +public class CorrelationRuleFilter extends RuleFilter { + private final Map properties = new HashMap<>(); + private String correlationId; + private String contentType; + private String label; + private String messageId; + private String replyTo; + private String replyToSessionId; + private String sessionId; + private String to; + + /** + * Initializes a new instance of {@link CorrelationRuleFilter} with default values. + */ + public CorrelationRuleFilter() { + this.correlationId = null; + } + + /** + * Initializes a new instance of {@link CorrelationRuleFilter} with default values with the specified correlation + * identifier. + * + * @param correlationId The identifier for the correlation. + * + * @throws IllegalArgumentException If {@code correlationId} is an empty string. + * @throws NullPointerException If {@code correlationId} is null. + */ + public CorrelationRuleFilter(String correlationId) { + final ClientLogger logger = new ClientLogger(CorrelationRuleFilter.class); + if (correlationId == null) { + throw logger.logExceptionAsError(new NullPointerException("'correlationId' cannot be null")); + } else if (correlationId.isEmpty()) { + throw logger.logExceptionAsError(new IllegalArgumentException("'correlationId' cannot be empty.")); + } + + this.correlationId = correlationId; + } + + /** + * Gets the content type of the message. + * + * @return The content type of the message. + */ + public String getContentType() { + return contentType; + } + + /** + * Sets the content type of the message. + * + * @param contentType The content type of the message. + * + * @return The updated {@link CorrelationRuleFilter} itself. + */ + public CorrelationRuleFilter setContentType(String contentType) { + this.contentType = contentType; + return this; + } + + /** + * Gets the correlation identifier. + * + * @return The correlation identifier. + */ + public String getCorrelationId() { + return correlationId; + } + + /** + * Sets the correlation identifier. + * + * @param correlationId The correlation identifier. + * + * @return The updated {@link CorrelationRuleFilter} itself. + */ + public CorrelationRuleFilter setCorrelationId(String correlationId) { + this.correlationId = correlationId; + return this; + } + + /** + * Gets the application specific label. + * + * @return The application specific label. + */ + public String getLabel() { + return label; + } + + /** + * Sets the application specific label. + * + * @param label The application specific label. + * + * @return The updated {@link CorrelationRuleFilter} itself. + */ + public CorrelationRuleFilter setLabel(String label) { + this.label = label; + return this; + } + + /** + * Gets the identifier for the message. + * + * @return The identifier for the message. + */ + public String getMessageId() { + return messageId; + } + + /** + * Sets the identifier for the message. + * + * @param messageId The identifier for the message. + * + * @return The updated {@link CorrelationRuleFilter} itself. + */ + public CorrelationRuleFilter setMessageId(String messageId) { + this.messageId = messageId; + return this; + } + + /** + * Gets application specific properties of the message. + * + * @return The application specific properties of the message. + */ + public Map getProperties() { + return properties; + } + + /** + * Gets the address of the queue or subscription to reply to. + * + * @return The address of the queue or subscription to reply to. + */ + public String getReplyTo() { + return replyTo; + } + + /** + * Sets the address of the queue or subscription to reply to. + * + * @param replyTo The address of the queue or subscription to reply to. + * + * @return The updated {@link CorrelationRuleFilter} itself. + */ + public CorrelationRuleFilter setReplyTo(String replyTo) { + this.replyTo = replyTo; + return this; + } + + /** + * Gets the session identifier to reply to. + * + * @return The session identifier to reply to. + */ + public String getReplyToSessionId() { + return replyToSessionId; + } + + /** + * Sets the session identifier to reply to. Max size of {@code replyToSessionId} is 128. + * + * @param replyToSessionId The session identifier to reply to. + * + * @return The updated {@link CorrelationRuleFilter} itself. + */ + public CorrelationRuleFilter setReplyToSessionId(String replyToSessionId) { + this.replyToSessionId = replyToSessionId; + return this; + } + + /** + * Gets the session identifier. + * + * @return The session identifier. + */ + public String getSessionId() { + return sessionId; + } + + /** + * Sets the session identifier. Max size of {@code sessionId} is 128 chars. + * + * @param sessionId The session identifier. + * + * @return The updated {@link CorrelationRuleFilter} itself. + */ + public CorrelationRuleFilter setSessionId(String sessionId) { + this.sessionId = sessionId; + return this; + } + + /** + * Gets the address to send to. + * + * @return The address to send to. + */ + public String getTo() { + return to; + } + + /** + * Sets the address to send to. + * + * @param to The address to send to. + * + * @return The updated {@link CorrelationRuleFilter} itself. + */ + public CorrelationRuleFilter setTo(String to) { + this.to = to; + return this; + } + + /** + * Converts the value of the current instance to its equivalent string representation. + * + * @return A string representation of the current instance. + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder("CorrelationRuleFilter: "); + + boolean isFirstExpression = appendPropertyExpression(true, builder, "sys.CorrelationId", + correlationId); + isFirstExpression = appendPropertyExpression(isFirstExpression, builder, "sys.MessageId", + messageId); + isFirstExpression = appendPropertyExpression(isFirstExpression, builder, "sys.To", to); + isFirstExpression = appendPropertyExpression(isFirstExpression, builder, "sys.ReplyTo", replyTo); + isFirstExpression = appendPropertyExpression(isFirstExpression, builder, "sys.Label", label); + isFirstExpression = appendPropertyExpression(isFirstExpression, builder, "sys.SessionId", sessionId); + isFirstExpression = appendPropertyExpression(isFirstExpression, builder, "sys.ReplyToSessionId", + replyToSessionId); + isFirstExpression = appendPropertyExpression(isFirstExpression, builder, "sys.ContentType", + contentType); + + for (Map.Entry entry : properties.entrySet()) { + isFirstExpression = appendPropertyExpression(isFirstExpression, builder, entry.getKey(), + entry.getValue().toString()); + } + + return builder.toString(); + } + + private static boolean appendPropertyExpression(boolean isFirstExpression, StringBuilder builder, String display, + String value) { + + if (value == null) { + return true; + } + + if (!isFirstExpression) { + builder.append(" AND "); + } + + builder.append(String.format("%s = '%s'", display, value)); + return false; + } +} diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/CreateRuleOptions.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/CreateRuleOptions.java new file mode 100644 index 0000000000000..f293788cec932 --- /dev/null +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/CreateRuleOptions.java @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.messaging.servicebus.administration.models; + +import com.azure.core.annotation.Fluent; + +import java.util.Objects; + +/** + * A set of options for creating a rule. + */ +@Fluent +public class CreateRuleOptions { + private RuleFilter filter; + private RuleAction action; + + /** + * Initializes a new instance with the {@link TrueRuleFilter}. + */ + public CreateRuleOptions() { + this(TrueRuleFilter.getInstance()); + } + + /** + * Initializes a new instance with the given rule {@code name} and {@code filter}. + * + * @param filter Filter expression used to match messages. + * @throws NullPointerException if {@code filter} is null. + */ + public CreateRuleOptions(RuleFilter filter) { + this.filter = Objects.requireNonNull(filter, "'filter' cannot be null."); + } + + /** + * Initializes a new instance with the given rule properties. + * + * @param ruleProperties Rule properties to create new rule from. + * @throws NullPointerException if {@code ruleProperties} is null. + */ + public CreateRuleOptions(RuleProperties ruleProperties) { + Objects.requireNonNull(ruleProperties, "'ruleProperties' cannot be null."); + + this.filter = ruleProperties.getFilter(); + this.action = ruleProperties.getAction(); + } + + /** + * Gets the action to perform if the message satisfies the filtering expression. + * + * @return The action to perform if the message satisfies the filtering expression. + */ + public RuleAction getAction() { + return action; + } + + /** + * Sets the action to perform if the message satisfies the filtering expression. + * + * @param action The action to perform if the message satisfies the filtering expression. + * @return The updated {@link CreateRuleOptions} object. + */ + public CreateRuleOptions setAction(RuleAction action) { + this.action = action; + return this; + } + + /** + * Gets the filter expression used to match messages. + * + * @return The filter expression used to match messages. + */ + public RuleFilter getFilter() { + return filter; + } + + /** + * Sets the filter expression used to match messages. + * + * @param filter The filter expression used to match messages. + * @return The updated {@link CreateRuleOptions} object. + */ + public CreateRuleOptions setFilter(RuleFilter filter) { + this.filter = Objects.requireNonNull(filter, "'filter' cannot be null."); + return this; + } +} diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/EmptyRuleAction.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/EmptyRuleAction.java new file mode 100644 index 0000000000000..e79d2b62254c6 --- /dev/null +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/EmptyRuleAction.java @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.messaging.servicebus.administration.models; + +/** + * Represents a rule action that does nothing. + */ +public class EmptyRuleAction extends RuleAction { + private static final EmptyRuleAction INSTANCE = new EmptyRuleAction(); + + static EmptyRuleAction getInstance() { + return INSTANCE; + } +} diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/FalseRuleFilter.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/FalseRuleFilter.java new file mode 100644 index 0000000000000..33cda84c0cb75 --- /dev/null +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/FalseRuleFilter.java @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.messaging.servicebus.administration.models; + +/** + * Matches none the messages arriving to be selected for the subscription. + */ +public class FalseRuleFilter extends SqlRuleFilter { + private static final FalseRuleFilter INSTANCE = new FalseRuleFilter(); + + /** + * Gets an instance of the {@link FalseRuleFilter}. + * + * @return an instance of the {@link FalseRuleFilter}. + */ + static FalseRuleFilter getInstance() { + return INSTANCE; + } + + /** + * Initializes a new instance. + */ + public FalseRuleFilter() { + super("1=0"); + } + + /** + * Converts the current instance to its string representation. + * + * @return The string representation. + */ + @Override + public String toString() { + return "FalseRuleFilter"; + } +} diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/RuleAction.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/RuleAction.java new file mode 100644 index 0000000000000..1b5735fe1ba6b --- /dev/null +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/RuleAction.java @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.messaging.servicebus.administration.models; + +/** + * Represents the filter actions which are allowed for the transformation of a message that have been matched by a + * filter expression. + * + *

+ * Filter actions allow for the transformation of a message that have been matched by a filter expression. The typical + * use case for filter actions is to append or update the properties that are attached to a message, for example + * assigning a group ID based on the correlation ID of a message. + *

+ */ +public class RuleAction { + RuleAction() { + // This is intentionally left blank. This constructor exists + // only to prevent external assemblies inheriting from it. + } +} diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/RuleFilter.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/RuleFilter.java new file mode 100644 index 0000000000000..a5158119f7cd1 --- /dev/null +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/RuleFilter.java @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.messaging.servicebus.administration.models; + +import com.azure.messaging.servicebus.ServiceBusMessage; + +/** + * Describes a filter expression that is evaluated against a {@link ServiceBusMessage}. Filter is an abstract class with + * the following concrete implementations: + *
    + *
  • {@link SqlRuleFilter} that represents a filter using SQL syntax.
  • + *
  • {@link CorrelationRuleFilter} that provides an optimisation for correlation equality expressions.
  • + *
+ * + * @see SqlRuleFilter + * @see CorrelationRuleFilter + * @see TrueRuleFilter + * @see FalseRuleFilter + */ +public abstract class RuleFilter { + RuleFilter() { + // This is intentionally left blank. This constructor exists + // only to prevent external assemblies inheriting from it. + } +} diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/RuleProperties.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/RuleProperties.java new file mode 100644 index 0000000000000..73bdc8178230d --- /dev/null +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/RuleProperties.java @@ -0,0 +1,270 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.messaging.servicebus.administration.models; + +import com.azure.core.annotation.Fluent; +import com.azure.messaging.servicebus.implementation.EntityHelper; +import com.azure.messaging.servicebus.implementation.models.CorrelationFilterImpl; +import com.azure.messaging.servicebus.implementation.models.EmptyRuleActionImpl; +import com.azure.messaging.servicebus.implementation.models.FalseFilterImpl; +import com.azure.messaging.servicebus.implementation.models.KeyValueImpl; +import com.azure.messaging.servicebus.implementation.models.RuleActionImpl; +import com.azure.messaging.servicebus.implementation.models.RuleDescription; +import com.azure.messaging.servicebus.implementation.models.RuleFilterImpl; +import com.azure.messaging.servicebus.implementation.models.SqlFilterImpl; +import com.azure.messaging.servicebus.implementation.models.SqlRuleActionImpl; +import com.azure.messaging.servicebus.implementation.models.TrueFilterImpl; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * Represents the properties of a rule. + */ +@Fluent +public class RuleProperties { + private final String name; + private RuleFilter filter; + private RuleAction action; + + static { + EntityHelper.setRuleAccessor(new EntityHelper.RuleAccessor() { + private final EmptyRuleActionImpl emptyRuleAction = new EmptyRuleActionImpl(); + private final SqlFilterImpl trueFilter = new TrueFilterImpl().setSqlExpression("1=1"); + private final SqlFilterImpl falseFilter = new TrueFilterImpl().setSqlExpression("1=0"); + + @Override + public RuleProperties toModel(RuleDescription description) { + final RuleFilter filter = description.getFilter() != null + ? toModel(description.getFilter()) + : null; + final RuleAction action = description.getAction() != null + ? toModel(description.getAction()) + : null; + + return new RuleProperties(description.getName(), filter, action); + } + + @Override + public RuleAction toModel(RuleActionImpl implementation) { + if (implementation instanceof EmptyRuleActionImpl) { + return EmptyRuleAction.getInstance(); + } else if (implementation instanceof SqlRuleActionImpl) { + final SqlRuleActionImpl action = (SqlRuleActionImpl) implementation; + final SqlRuleAction returned = new SqlRuleAction(action.getSqlExpression(), + action.getCompatibilityLevel(), action.isRequiresPreprocessing()); + + if (action.getParameters() != null) { + for (KeyValueImpl parameter : action.getParameters()) { + returned.getProperties().put(parameter.getKey(), parameter.getValue()); + } + } + + return returned; + } else { + return null; + } + } + + @Override + public RuleFilter toModel(RuleFilterImpl implementation) { + if (implementation instanceof TrueFilterImpl) { + return TrueRuleFilter.getInstance(); + } else if (implementation instanceof FalseFilterImpl) { + return FalseRuleFilter.getInstance(); + } else if (implementation instanceof CorrelationFilterImpl) { + final CorrelationFilterImpl filter = (CorrelationFilterImpl) implementation; + final CorrelationRuleFilter returned = new CorrelationRuleFilter() + .setContentType(filter.getContentType()) + .setCorrelationId(filter.getCorrelationId()) + .setLabel(filter.getLabel()) + .setMessageId(filter.getMessageId()) + .setTo(filter.getTo()) + .setSessionId(filter.getSessionId()) + .setReplyTo(filter.getReplyTo()) + .setReplyToSessionId(filter.getReplyToSessionId()); + + if (filter.getProperties() != null) { + filter.getProperties().forEach(keyValue -> + returned.getProperties().put(keyValue.getKey(), keyValue.getValue())); + } + + return returned; + } else if (implementation instanceof SqlFilterImpl) { + final SqlFilterImpl filter = (SqlFilterImpl) implementation; + final SqlRuleFilter returned = new SqlRuleFilter(filter.getSqlExpression(), + filter.getCompatibilityLevel(), filter.isRequiresPreprocessing()); + + if (filter.getParameters() != null) { + filter.getParameters().forEach(keyValue -> + returned.getProperties().put(keyValue.getKey(), keyValue.getValue())); + } + + return returned; + } else { + return null; + } + } + + @Override + public RuleDescription toImplementation(RuleProperties ruleProperties) { + final RuleFilterImpl filter = ruleProperties.getFilter() != null + ? toImplementation(ruleProperties.getFilter()) + : null; + final RuleActionImpl action = ruleProperties.getAction() != null + ? toImplementation(ruleProperties.getAction()) + : null; + + return new RuleDescription() + .setName(ruleProperties.getName()) + .setAction(action) + .setFilter(filter); + } + + @Override + public RuleActionImpl toImplementation(RuleAction model) { + if (model instanceof EmptyRuleAction) { + return emptyRuleAction; + } else if (model instanceof SqlRuleAction) { + final SqlRuleAction action = (SqlRuleAction) model; + final SqlRuleActionImpl returned = new SqlRuleActionImpl() + .setSqlExpression(action.getSqlExpression()) + .setCompatibilityLevel(action.getCompatibilityLevel()) + .setRequiresPreprocessing(action.getRequiresPreprocessing()); + + if (!action.getProperties().isEmpty()) { + final List parameters = action.getProperties().entrySet().stream() + .map(entry -> new KeyValueImpl() + .setKey(entry.getKey()).setValue(entry.getValue().toString())) + .collect(Collectors.toList()); + + returned.setParameters(parameters); + } + + return returned; + } else { + return null; + } + } + + @Override + public RuleFilterImpl toImplementation(RuleFilter model) { + if (model instanceof TrueRuleFilter) { + return trueFilter; + } else if (model instanceof FalseRuleFilter) { + return falseFilter; + } else if (model instanceof CorrelationRuleFilter) { + final CorrelationRuleFilter filter = (CorrelationRuleFilter) model; + final CorrelationFilterImpl returned = new CorrelationFilterImpl() + .setContentType(filter.getContentType()) + .setCorrelationId(filter.getCorrelationId()) + .setLabel(filter.getLabel()) + .setMessageId(filter.getMessageId()) + .setTo(filter.getTo()) + .setSessionId(filter.getSessionId()) + .setReplyTo(filter.getReplyTo()) + .setReplyToSessionId(filter.getReplyToSessionId()); + + if (!filter.getProperties().isEmpty()) { + final List parameters = filter.getProperties().entrySet() + .stream() + .map(entry -> new KeyValueImpl() + .setKey(entry.getKey()).setValue(entry.getValue().toString())) + .collect(Collectors.toList()); + + returned.setProperties(parameters); + } + + return returned; + } else if (model instanceof SqlRuleFilter) { + final SqlRuleFilter filter = (SqlRuleFilter) model; + final SqlFilterImpl returned = new SqlFilterImpl() + .setSqlExpression(filter.getSqlExpression()) + .setCompatibilityLevel(filter.getCompatibilityLevel()) + .setRequiresPreprocessing(filter.getRequiresPreprocessing()); + + if (!filter.getProperties().isEmpty()) { + final List parameters = filter.getProperties().entrySet() + .stream() + .map(entry -> new KeyValueImpl() + .setKey(entry.getKey()).setValue(entry.getValue().toString())) + .collect(Collectors.toList()); + + returned.setParameters(parameters); + } + + return returned; + } else { + return null; + } + } + }); + } + + /** + * Initializes a new instance with the given rule {@code name}, {@code filter}, and {@code action}. + * + * @param name Name of the rule. + * @param filter Filter for the rule. + * @param action Action for the rule. + */ + RuleProperties(String name, RuleFilter filter, RuleAction action) { + this.name = name; + this.filter = filter; + this.action = action; + } + + /** + * Gets the filter expression used to match messages. + * + * @return The filter expression used to match messages. + */ + public RuleFilter getFilter() { + return filter; + } + + /** + * Sets the filter expression used to match messages. + * + * @param filter the filter expression used to match messages. + * + * @return The updated {@link RuleProperties} object itself. + * @throws NullPointerException if {@code filter} is null. + */ + public RuleProperties setFilter(RuleFilter filter) { + this.filter = Objects.requireNonNull(filter, "'filter' cannot be null."); + return this; + } + + /** + * Gets the name of the rule. + * + * @return The name of the rule. + */ + public String getName() { + return name; + } + + /** + * Gets the action to perform if the message satisfies the filtering expression. + * + * @return The action to perform if the message satisfies the filtering expression. + */ + public RuleAction getAction() { + return action; + } + + /** + * Sets the action to perform if the message satisfies the filtering expression. + * + * @param action The action to perform if the message satisfies the filtering expression. + * + * @return The updated {@link RuleProperties} object itself. + */ + public RuleProperties setAction(RuleAction action) { + this.action = action; + return this; + } +} diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/SqlRuleAction.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/SqlRuleAction.java new file mode 100644 index 0000000000000..ecfe30598dab4 --- /dev/null +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/SqlRuleAction.java @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.messaging.servicebus.administration.models; + +import com.azure.core.util.logging.ClientLogger; +import com.azure.messaging.servicebus.ServiceBusMessage; + +import java.util.HashMap; +import java.util.Map; + +/** + * Represents set of actions written in SQL language-based syntax that is performed against a {@link + * ServiceBusMessage}. + */ +public class SqlRuleAction extends RuleAction { + private final Map properties = new HashMap<>(); + private final String sqlExpression; + private final String compatibilityLevel; + private final Boolean requiresPreprocessing; + + /** + * Creates a new instance with the given SQL expression. + * + * @param sqlExpression SQL expression for the action. + * + * @throws NullPointerException if {@code sqlExpression} is null. + * @throws IllegalArgumentException if {@code sqlExpression} is an empty string. + */ + public SqlRuleAction(String sqlExpression) { + final ClientLogger logger = new ClientLogger(SqlRuleAction.class); + + if (sqlExpression == null) { + throw logger.logExceptionAsError(new NullPointerException("'sqlExpression' cannot be null.")); + } else if (sqlExpression.isEmpty()) { + throw logger.logExceptionAsError( + new IllegalArgumentException("'sqlExpression' cannot be an empty string.")); + } + + this.sqlExpression = sqlExpression; + this.compatibilityLevel = null; + this.requiresPreprocessing = null; + } + + /** + * Package private constructor for creating a model deserialised from the service. + * + * @param sqlExpression SQL expression for the action. + * @param compatibilityLevel The compatibility level. + * @param requiresPreprocessing Whether or not it requires preprocessing + */ + SqlRuleAction(String sqlExpression, String compatibilityLevel, Boolean requiresPreprocessing) { + this.sqlExpression = sqlExpression; + this.compatibilityLevel = compatibilityLevel; + this.requiresPreprocessing = requiresPreprocessing; + } + + /** + * Gets the compatibility level. + * + * @return The compatibility level. + */ + String getCompatibilityLevel() { + return compatibilityLevel; + } + + /** + * Gets whether or not requires preprocessing. + * + * @return Whether or not requires preprocessing. + */ + Boolean getRequiresPreprocessing() { + return requiresPreprocessing; + } + + /** + * Gets the properties for this action. + * + * @return the properties for this action. + */ + public Map getProperties() { + return properties; + } + + /** + * Gets the SQL expression. + * + * @return the SQL expression. + */ + public String getSqlExpression() { + return sqlExpression; + } +} diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/SqlRuleFilter.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/SqlRuleFilter.java new file mode 100644 index 0000000000000..32630779325c0 --- /dev/null +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/SqlRuleFilter.java @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.messaging.servicebus.administration.models; + +import com.azure.core.util.logging.ClientLogger; +import com.azure.messaging.servicebus.ServiceBusMessage; + +import java.util.HashMap; +import java.util.Map; + +/** + * Represents a filter which is a composition of an expression and an action that is executed in the pub/sub pipeline. + *

+ * A {@link SqlRuleFilter} holds a SQL-like condition expression that is evaluated in the broker against the arriving + * messages' user-defined properties and system properties. All system properties (which are all properties explicitly + * listed on the {@link ServiceBusMessage} class) must be prefixed with {@code sys.} in the condition expression. The + * SQL subset implements testing for existence of properties (EXISTS), testing for null-values (IS NULL), logical + * NOT/AND/OR, relational operators, numeric arithmetic, and simple text pattern matching with LIKE. + *

+ */ +public class SqlRuleFilter extends RuleFilter { + private final Map properties = new HashMap<>(); + private final String sqlExpression; + private final String compatibilityLevel; + private final Boolean requiresPreprocessing; + + /** + * Creates a new instance with the given SQL expression. + * + * @param sqlExpression SQL expression for the filter. + * + * @throws NullPointerException if {@code sqlExpression} is null. + * @throws IllegalArgumentException if {@code sqlExpression} is an empty string. + */ + public SqlRuleFilter(String sqlExpression) { + final ClientLogger logger = new ClientLogger(SqlRuleFilter.class); + + if (sqlExpression == null) { + throw logger.logExceptionAsError(new NullPointerException("'sqlExpression' cannot be null.")); + } else if (sqlExpression.isEmpty()) { + throw logger.logExceptionAsError( + new IllegalArgumentException("'sqlExpression' cannot be an empty string.")); + } + + this.sqlExpression = sqlExpression; + this.compatibilityLevel = null; + this.requiresPreprocessing = null; + } + + /** + * Package private constructor for creating a model deserialised from the service. + * + * @param sqlExpression SQL expression for the filter. + * @param compatibilityLevel The compatibility level. + * @param requiresPreprocessing Whether or not it requires preprocessing + */ + SqlRuleFilter(String sqlExpression, String compatibilityLevel, Boolean requiresPreprocessing) { + this.sqlExpression = sqlExpression; + this.compatibilityLevel = compatibilityLevel; + this.requiresPreprocessing = requiresPreprocessing; + } + + /** + * Gets the compatibility level. + * + * @return The compatibility level. + */ + String getCompatibilityLevel() { + return compatibilityLevel; + } + + /** + * Gets whether or not requires preprocessing. + * + * @return Whether or not requires preprocessing. + */ + Boolean getRequiresPreprocessing() { + return requiresPreprocessing; + } + + /** + * Gets the value of a filter expression. Allowed types: string, int, long, bool, double + * + * @return Gets the value of a filter expression. + */ + public Map getProperties() { + return properties; + } + + /** + * Gets the SQL expression. + * + * @return The SQL expression. + */ + public String getSqlExpression() { + return sqlExpression; + } +} diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/TrueRuleFilter.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/TrueRuleFilter.java new file mode 100644 index 0000000000000..ffec93754f815 --- /dev/null +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/TrueRuleFilter.java @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.messaging.servicebus.administration.models; + +/** + * Matches all the messages arriving to be selected for the subscription. + */ +public final class TrueRuleFilter extends SqlRuleFilter { + private static final TrueRuleFilter INSTANCE = new TrueRuleFilter(); + + /** + * Gets an instance of the {@link TrueRuleFilter}. + * + * @return an instance of the {@link TrueRuleFilter}. + */ + static TrueRuleFilter getInstance() { + return INSTANCE; + } + + /** + * Initializes a new instance. + */ + public TrueRuleFilter() { + super("1=1"); + } + + /** + * Converts the current instance to its string representation. + * + * @return The string representation. + */ + @Override + public String toString() { + return "TrueRuleFilter"; + } +} diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/EntityHelper.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/EntityHelper.java index 57ac39a9bbe66..f92bc31b5169a 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/EntityHelper.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/EntityHelper.java @@ -8,9 +8,15 @@ import com.azure.messaging.servicebus.administration.models.CreateSubscriptionOptions; import com.azure.messaging.servicebus.administration.models.CreateTopicOptions; import com.azure.messaging.servicebus.administration.models.QueueProperties; +import com.azure.messaging.servicebus.administration.models.RuleAction; +import com.azure.messaging.servicebus.administration.models.RuleFilter; +import com.azure.messaging.servicebus.administration.models.RuleProperties; import com.azure.messaging.servicebus.administration.models.SubscriptionProperties; import com.azure.messaging.servicebus.administration.models.TopicProperties; import com.azure.messaging.servicebus.implementation.models.QueueDescription; +import com.azure.messaging.servicebus.implementation.models.RuleActionImpl; +import com.azure.messaging.servicebus.implementation.models.RuleDescription; +import com.azure.messaging.servicebus.implementation.models.RuleFilterImpl; import com.azure.messaging.servicebus.implementation.models.SubscriptionDescription; import com.azure.messaging.servicebus.implementation.models.TopicDescription; @@ -23,6 +29,7 @@ public final class EntityHelper { private static QueueAccessor queueAccessor; private static SubscriptionAccessor subscriptionAccessor; private static TopicAccessor topicAccessor; + private static RuleAccessor ruleAccessor; static { try { @@ -30,33 +37,17 @@ public final class EntityHelper { Class.forName(SubscriptionProperties.class.getName(), true, SubscriptionProperties.class.getClassLoader()); Class.forName(TopicProperties.class.getName(), true, TopicProperties.class.getClassLoader()); + Class.forName(RuleProperties.class.getName(), true, RuleProperties.class.getClassLoader()); } catch (ClassNotFoundException e) { throw new ClientLogger(EntityHelper.class).logExceptionAsError(new IllegalStateException(e)); } } - /** - * Creates a new topic given the options. - * - * @param description Options to create topic with. - * - * @return A new {@link TopicProperties} with the set options. - */ - public static TopicProperties toModel(TopicDescription description) { - Objects.requireNonNull(description, "'description' cannot be null."); - - if (topicAccessor == null) { - throw new ClientLogger(EntityHelper.class).logExceptionAsError( - new IllegalStateException("'topicAccessor' should not be null.")); - } - - return topicAccessor.toModel(description); - } - /** * Gets a queue description given the options. * * @param options The options. + * * @return The corresponding queue. */ public static QueueDescription getQueueDescription(CreateQueueOptions options) { @@ -115,6 +106,7 @@ public static TopicDescription getTopicDescription(CreateTopicOptions options) { * Creates a new queue given the existing queue. * * @param description Options to create queue with. + * * @return A new {@link QueueProperties} with the set options. */ public static QueueDescription toImplementation(QueueProperties description) { @@ -128,10 +120,62 @@ public static QueueDescription toImplementation(QueueProperties description) { return queueAccessor.toImplementation(description); } + /** + * Creates a new rule action given an existing rule action. + * + * @param properties Rule properties. + * @return A new instance of {@link RuleActionImpl}. + */ + public static RuleActionImpl toImplementation(RuleAction properties) { + Objects.requireNonNull(properties, "'properties' cannot be null."); + + if (ruleAccessor == null) { + throw new ClientLogger(EntityHelper.class).logExceptionAsError( + new IllegalStateException("'ruleAccessor' should not be null.")); + } + + return ruleAccessor.toImplementation(properties); + } + + /** + * Creates a new rule description given an existing rule. + * + * @param properties Rule properties. + * @return A new instance of {@link RuleDescription}. + */ + public static RuleDescription toImplementation(RuleProperties properties) { + Objects.requireNonNull(properties, "'properties' cannot be null."); + + if (ruleAccessor == null) { + throw new ClientLogger(EntityHelper.class).logExceptionAsError( + new IllegalStateException("'ruleAccessor' should not be null.")); + } + + return ruleAccessor.toImplementation(properties); + } + + /** + * Creates a new rule filter given an existing rule filter. + * + * @param properties Rule filter. + * @return A new instance of {@link RuleFilter}. + */ + public static RuleFilterImpl toImplementation(RuleFilter properties) { + Objects.requireNonNull(properties, "'properties' cannot be null."); + + if (ruleAccessor == null) { + throw new ClientLogger(EntityHelper.class).logExceptionAsError( + new IllegalStateException("'ruleAccessor' should not be null.")); + } + + return ruleAccessor.toImplementation(properties); + } + /** * Creates a new queue given the existing queue. * * @param description Options to create queue with. + * * @return A new {@link SubscriptionProperties} with the set options. */ public static SubscriptionDescription toImplementation(SubscriptionProperties description) { @@ -149,6 +193,7 @@ public static SubscriptionDescription toImplementation(SubscriptionProperties de * Creates a new queue given the existing queue. * * @param properties Options to create queue with. + * * @return A new {@link TopicProperties} with the set options. */ public static TopicDescription toImplementation(TopicProperties properties) { @@ -166,6 +211,7 @@ public static TopicDescription toImplementation(TopicProperties properties) { * Creates a new queue given the existing queue. * * @param description Options to create queue with. + * * @return A new {@link QueueProperties} with the set options. */ public static QueueProperties toModel(QueueDescription description) { @@ -179,6 +225,57 @@ public static QueueProperties toModel(QueueDescription description) { return queueAccessor.toModel(description); } + /** + * Gets a new rule action based on the existing rule description. + * + * @param description The implementation type. + * @return A new {@link RuleProperties} with the set options. + */ + public static RuleAction toModel(RuleActionImpl description) { + Objects.requireNonNull(description, "'description' cannot be null."); + + if (ruleAccessor == null) { + throw new ClientLogger(EntityHelper.class).logExceptionAsError( + new IllegalStateException("'ruleAccessor' should not be null.")); + } + + return ruleAccessor.toModel(description); + } + + /** + * Gets a new rule action based on the existing rule description. + * + * @param description The implementation type. + * @return A new {@link RuleProperties} with the set options. + */ + public static RuleFilter toModel(RuleFilterImpl description) { + Objects.requireNonNull(description, "'description' cannot be null."); + + if (ruleAccessor == null) { + throw new ClientLogger(EntityHelper.class).logExceptionAsError( + new IllegalStateException("'ruleAccessor' should not be null.")); + } + + return ruleAccessor.toModel(description); + } + + /** + * Gets a new rule based on the existing rule description. + * + * @param description The implementation type. + * @return A new {@link RuleProperties} with the set options. + */ + public static RuleProperties toModel(RuleDescription description) { + Objects.requireNonNull(description, "'description' cannot be null."); + + if (ruleAccessor == null) { + throw new ClientLogger(EntityHelper.class).logExceptionAsError( + new IllegalStateException("'ruleAccessor' should not be null.")); + } + + return ruleAccessor.toModel(description); + } + /** * Creates a new subscription given the options. * @@ -197,6 +294,24 @@ public static SubscriptionProperties toModel(SubscriptionDescription options) { return subscriptionAccessor.toModel(options); } + /** + * Creates a new topic given the options. + * + * @param description Options to create topic with. + * + * @return A new {@link TopicProperties} with the set options. + */ + public static TopicProperties toModel(TopicDescription description) { + Objects.requireNonNull(description, "'description' cannot be null."); + + if (topicAccessor == null) { + throw new ClientLogger(EntityHelper.class).logExceptionAsError( + new IllegalStateException("'topicAccessor' should not be null.")); + } + + return topicAccessor.toModel(description); + } + /** * Sets the queue accessor. * @@ -228,6 +343,22 @@ public static void setQueueName(QueueProperties queueProperties, String name) { queueAccessor.setName(queueProperties, name); } + /** + * Sets the rule accessor. + * + * @param accessor The rule accessor. + */ + public static void setRuleAccessor(RuleAccessor accessor) { + Objects.requireNonNull(accessor, "'accessor' cannot be null."); + + if (EntityHelper.ruleAccessor != null) { + throw new ClientLogger(EntityHelper.class).logExceptionAsError(new IllegalStateException( + "'ruleAccessor' is already set.")); + } + + EntityHelper.ruleAccessor = accessor; + } + /** * Sets the subscription accessor. * @@ -313,6 +444,7 @@ public interface QueueAccessor { * Creates a new queue from the given {@code queueDescription}. * * @param queueDescription Queue description to use. + * * @return A new queue with the properties set. */ QueueDescription toImplementation(QueueProperties queueDescription); @@ -321,6 +453,7 @@ public interface QueueAccessor { * Creates a new queue from the given {@code queueDescription}. * * @param queueDescription Queue description to use. + * * @return A new queue with the properties set. */ QueueProperties toModel(QueueDescription queueDescription); @@ -334,6 +467,23 @@ public interface QueueAccessor { void setName(QueueProperties queueProperties, String name); } + /** + * Interface for accessing methods on a rule. + */ + public interface RuleAccessor { + RuleProperties toModel(RuleDescription ruleDescription); + + RuleAction toModel(RuleActionImpl implementation); + + RuleFilter toModel(RuleFilterImpl implementation); + + RuleDescription toImplementation(RuleProperties ruleProperties); + + RuleActionImpl toImplementation(RuleAction model); + + RuleFilterImpl toImplementation(RuleFilter model); + } + /** * Interface for accessing methods on a subscription. */ @@ -342,6 +492,7 @@ public interface SubscriptionAccessor { * Creates a model subscription with the given implementation. * * @param subscription Options used to create subscription. + * * @return A new subscription. */ SubscriptionProperties toModel(SubscriptionDescription subscription); @@ -350,6 +501,7 @@ public interface SubscriptionAccessor { * Creates the implementation subscription with the given subscription. * * @param subscription Options used to create subscription. + * * @return A new subscription. */ SubscriptionDescription toImplementation(SubscriptionProperties subscription); diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/CorrelationFilter.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/CorrelationFilterImpl.java similarity index 89% rename from sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/CorrelationFilter.java rename to sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/CorrelationFilterImpl.java index 251fa2c67607f..fdcf4f72f73ed 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/CorrelationFilter.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/CorrelationFilterImpl.java @@ -19,7 +19,7 @@ @JsonTypeName("CorrelationFilter") @JacksonXmlRootElement(localName = "CorrelationFilter") @Fluent -public final class CorrelationFilter extends RuleFilter { +public final class CorrelationFilterImpl extends RuleFilterImpl { /* * The correlationId property. */ @@ -86,10 +86,10 @@ public final class CorrelationFilter extends RuleFilter { private static final class PropertiesWrapper { @JacksonXmlProperty(localName = "KeyValueOfstringanyType") - private final List items; + private final List items; @JsonCreator - private PropertiesWrapper(@JacksonXmlProperty(localName = "KeyValueOfstringanyType") List items) { + private PropertiesWrapper(@JacksonXmlProperty(localName = "KeyValueOfstringanyType") List items) { this.items = items; } } @@ -117,7 +117,7 @@ public String getCorrelationId() { * @param correlationId the correlationId value to set. * @return the CorrelationFilter object itself. */ - public CorrelationFilter setCorrelationId(String correlationId) { + public CorrelationFilterImpl setCorrelationId(String correlationId) { this.correlationId = correlationId; return this; } @@ -137,7 +137,7 @@ public String getMessageId() { * @param messageId the messageId value to set. * @return the CorrelationFilter object itself. */ - public CorrelationFilter setMessageId(String messageId) { + public CorrelationFilterImpl setMessageId(String messageId) { this.messageId = messageId; return this; } @@ -157,7 +157,7 @@ public String getTo() { * @param to the to value to set. * @return the CorrelationFilter object itself. */ - public CorrelationFilter setTo(String to) { + public CorrelationFilterImpl setTo(String to) { this.to = to; return this; } @@ -177,7 +177,7 @@ public String getReplyTo() { * @param replyTo the replyTo value to set. * @return the CorrelationFilter object itself. */ - public CorrelationFilter setReplyTo(String replyTo) { + public CorrelationFilterImpl setReplyTo(String replyTo) { this.replyTo = replyTo; return this; } @@ -197,7 +197,7 @@ public String getLabel() { * @param label the label value to set. * @return the CorrelationFilter object itself. */ - public CorrelationFilter setLabel(String label) { + public CorrelationFilterImpl setLabel(String label) { this.label = label; return this; } @@ -217,7 +217,7 @@ public String getSessionId() { * @param sessionId the sessionId value to set. * @return the CorrelationFilter object itself. */ - public CorrelationFilter setSessionId(String sessionId) { + public CorrelationFilterImpl setSessionId(String sessionId) { this.sessionId = sessionId; return this; } @@ -237,7 +237,7 @@ public String getReplyToSessionId() { * @param replyToSessionId the replyToSessionId value to set. * @return the CorrelationFilter object itself. */ - public CorrelationFilter setReplyToSessionId(String replyToSessionId) { + public CorrelationFilterImpl setReplyToSessionId(String replyToSessionId) { this.replyToSessionId = replyToSessionId; return this; } @@ -257,7 +257,7 @@ public String getContentType() { * @param contentType the contentType value to set. * @return the CorrelationFilter object itself. */ - public CorrelationFilter setContentType(String contentType) { + public CorrelationFilterImpl setContentType(String contentType) { this.contentType = contentType; return this; } @@ -267,9 +267,9 @@ public CorrelationFilter setContentType(String contentType) { * * @return the properties value. */ - public List getProperties() { + public List getProperties() { if (this.properties == null) { - this.properties = new PropertiesWrapper(new ArrayList()); + this.properties = new PropertiesWrapper(new ArrayList()); } return this.properties.items; } @@ -280,7 +280,7 @@ public List getProperties() { * @param properties the properties value to set. * @return the CorrelationFilter object itself. */ - public CorrelationFilter setProperties(List properties) { + public CorrelationFilterImpl setProperties(List properties) { this.properties = new PropertiesWrapper(properties); return this; } diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/EmptyRuleAction.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/EmptyRuleActionImpl.java similarity index 91% rename from sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/EmptyRuleAction.java rename to sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/EmptyRuleActionImpl.java index 2abb4f7214b92..13a2393fbbc3d 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/EmptyRuleAction.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/EmptyRuleActionImpl.java @@ -14,5 +14,5 @@ @JsonTypeName("EmptyRuleAction") @JacksonXmlRootElement(localName = "EmptyRuleAction") @Immutable -public final class EmptyRuleAction extends RuleAction { +public final class EmptyRuleActionImpl extends RuleActionImpl { } diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/FalseFilter.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/FalseFilterImpl.java similarity index 91% rename from sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/FalseFilter.java rename to sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/FalseFilterImpl.java index 24098a84eb74b..522d582ffa445 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/FalseFilter.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/FalseFilterImpl.java @@ -14,5 +14,5 @@ @JsonTypeName("FalseFilter") @JacksonXmlRootElement(localName = "FalseFilter") @Immutable -public final class FalseFilter extends SqlFilter { +public final class FalseFilterImpl extends SqlFilterImpl { } diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/KeyValue.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/KeyValueImpl.java similarity index 80% rename from sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/KeyValue.java rename to sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/KeyValueImpl.java index 25385249652b5..dba97a407cc43 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/KeyValue.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/KeyValueImpl.java @@ -13,9 +13,9 @@ localName = "KeyValueOfstringanyType", namespace = "http://schemas.microsoft.com/netservices/2010/10/servicebus/connect") @Fluent -public final class KeyValue { +public final class KeyValueImpl { /* - * The Key property. + * The key property. */ @JacksonXmlProperty( localName = "Key", @@ -23,7 +23,7 @@ public final class KeyValue { private String key; /* - * The Value property. + * The value property. */ @JacksonXmlProperty( localName = "Value", @@ -31,7 +31,7 @@ public final class KeyValue { private String value; /** - * Get the key property: The Key property. + * Get the key property: The key property. * * @return the key value. */ @@ -40,18 +40,18 @@ public String getKey() { } /** - * Set the key property: The Key property. + * Set the key property: The key property. * * @param key the key value to set. * @return the KeyValue object itself. */ - public KeyValue setKey(String key) { + public KeyValueImpl setKey(String key) { this.key = key; return this; } /** - * Get the value property: The Value property. + * Get the value property: The value property. * * @return the value value. */ @@ -60,12 +60,12 @@ public String getValue() { } /** - * Set the value property: The Value property. + * Set the value property: The value property. * * @param value the value value to set. * @return the KeyValue object itself. */ - public KeyValue setValue(String value) { + public KeyValueImpl setValue(String value) { this.value = value; return this; } diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/RuleAction.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/RuleActionImpl.java similarity index 89% rename from sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/RuleAction.java rename to sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/RuleActionImpl.java index e5e8861291bec..d2434e6ae00b5 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/RuleAction.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/RuleActionImpl.java @@ -15,15 +15,15 @@ use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type", - defaultImpl = RuleAction.class) + defaultImpl = RuleActionImpl.class) @JsonTypeName("RuleAction") @JsonSubTypes({ - @JsonSubTypes.Type(name = "SqlRuleAction", value = SqlRuleAction.class), - @JsonSubTypes.Type(name = "EmptyRuleAction", value = EmptyRuleAction.class) + @JsonSubTypes.Type(name = "SqlRuleAction", value = SqlRuleActionImpl.class), + @JsonSubTypes.Type(name = "EmptyRuleAction", value = EmptyRuleActionImpl.class) }) @JacksonXmlRootElement( localName = "Action", namespace = "http://schemas.microsoft.com/netservices/2010/10/servicebus/connect") @Immutable -public class RuleAction { +public class RuleActionImpl { } diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/RuleDescription.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/RuleDescription.java index 5c7b98013c132..dbc7c2a419d26 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/RuleDescription.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/RuleDescription.java @@ -22,7 +22,7 @@ public final class RuleDescription { @JacksonXmlProperty( localName = "Filter", namespace = "http://schemas.microsoft.com/netservices/2010/10/servicebus/connect") - private RuleFilter filter; + private RuleFilterImpl filter; /* * The action property. @@ -30,7 +30,7 @@ public final class RuleDescription { @JacksonXmlProperty( localName = "Action", namespace = "http://schemas.microsoft.com/netservices/2010/10/servicebus/connect") - private RuleAction action; + private RuleActionImpl action; /* * The exact time the rule was created. @@ -53,7 +53,7 @@ public final class RuleDescription { * * @return the filter value. */ - public RuleFilter getFilter() { + public RuleFilterImpl getFilter() { return this.filter; } @@ -63,7 +63,7 @@ public RuleFilter getFilter() { * @param filter the filter value to set. * @return the RuleDescription object itself. */ - public RuleDescription setFilter(RuleFilter filter) { + public RuleDescription setFilter(RuleFilterImpl filter) { this.filter = filter; return this; } @@ -73,7 +73,7 @@ public RuleDescription setFilter(RuleFilter filter) { * * @return the action value. */ - public RuleAction getAction() { + public RuleActionImpl getAction() { return this.action; } @@ -83,7 +83,7 @@ public RuleAction getAction() { * @param action the action value to set. * @return the RuleDescription object itself. */ - public RuleDescription setAction(RuleAction action) { + public RuleDescription setAction(RuleActionImpl action) { this.action = action; return this; } diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/RuleFilter.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/RuleFilterImpl.java similarity index 84% rename from sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/RuleFilter.java rename to sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/RuleFilterImpl.java index 2197d41b2f0c0..96ba6dd77eeea 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/RuleFilter.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/RuleFilterImpl.java @@ -15,15 +15,15 @@ use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type", - defaultImpl = RuleFilter.class) + defaultImpl = RuleFilterImpl.class) @JsonTypeName("RuleFilter") @JsonSubTypes({ - @JsonSubTypes.Type(name = "CorrelationFilter", value = CorrelationFilter.class), - @JsonSubTypes.Type(name = "SqlFilter", value = SqlFilter.class) + @JsonSubTypes.Type(name = "CorrelationFilter", value = CorrelationFilterImpl.class), + @JsonSubTypes.Type(name = "SqlFilter", value = SqlFilterImpl.class) }) @JacksonXmlRootElement( localName = "Filter", namespace = "http://schemas.microsoft.com/netservices/2010/10/servicebus/connect") @Immutable -public class RuleFilter { +public class RuleFilterImpl { } diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/SqlFilter.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/SqlFilterImpl.java similarity index 87% rename from sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/SqlFilter.java rename to sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/SqlFilterImpl.java index fe4efde431b86..236ada8a9ccd0 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/SqlFilter.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/SqlFilterImpl.java @@ -20,15 +20,15 @@ use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type", - defaultImpl = SqlFilter.class) + defaultImpl = SqlFilterImpl.class) @JsonTypeName("SqlFilter") @JsonSubTypes({ - @JsonSubTypes.Type(name = "TrueFilter", value = TrueFilter.class), - @JsonSubTypes.Type(name = "FalseFilter", value = FalseFilter.class) + @JsonSubTypes.Type(name = "TrueFilter", value = TrueFilterImpl.class), + @JsonSubTypes.Type(name = "FalseFilter", value = FalseFilterImpl.class) }) @JacksonXmlRootElement(localName = "SqlFilter") @Fluent -public class SqlFilter extends RuleFilter { +public class SqlFilterImpl extends RuleFilterImpl { /* * The sqlExpression property. */ @@ -47,10 +47,10 @@ public class SqlFilter extends RuleFilter { private static final class ParametersWrapper { @JacksonXmlProperty(localName = "KeyValueOfstringanyType") - private final List items; + private final List items; @JsonCreator - private ParametersWrapper(@JacksonXmlProperty(localName = "KeyValueOfstringanyType") List items) { + private ParametersWrapper(@JacksonXmlProperty(localName = "KeyValueOfstringanyType") List items) { this.items = items; } } @@ -86,7 +86,7 @@ public String getSqlExpression() { * @param sqlExpression the sqlExpression value to set. * @return the SqlFilter object itself. */ - public SqlFilter setSqlExpression(String sqlExpression) { + public SqlFilterImpl setSqlExpression(String sqlExpression) { this.sqlExpression = sqlExpression; return this; } @@ -106,7 +106,7 @@ public String getCompatibilityLevel() { * @param compatibilityLevel the compatibilityLevel value to set. * @return the SqlFilter object itself. */ - public SqlFilter setCompatibilityLevel(String compatibilityLevel) { + public SqlFilterImpl setCompatibilityLevel(String compatibilityLevel) { this.compatibilityLevel = compatibilityLevel; return this; } @@ -116,9 +116,9 @@ public SqlFilter setCompatibilityLevel(String compatibilityLevel) { * * @return the parameters value. */ - public List getParameters() { + public List getParameters() { if (this.parameters == null) { - this.parameters = new ParametersWrapper(new ArrayList()); + this.parameters = new ParametersWrapper(new ArrayList()); } return this.parameters.items; } @@ -129,7 +129,7 @@ public List getParameters() { * @param parameters the parameters value to set. * @return the SqlFilter object itself. */ - public SqlFilter setParameters(List parameters) { + public SqlFilterImpl setParameters(List parameters) { this.parameters = new ParametersWrapper(parameters); return this; } @@ -149,7 +149,7 @@ public Boolean isRequiresPreprocessing() { * @param requiresPreprocessing the requiresPreprocessing value to set. * @return the SqlFilter object itself. */ - public SqlFilter setRequiresPreprocessing(Boolean requiresPreprocessing) { + public SqlFilterImpl setRequiresPreprocessing(Boolean requiresPreprocessing) { this.requiresPreprocessing = requiresPreprocessing; return this; } diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/SqlRuleAction.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/SqlRuleActionImpl.java similarity index 88% rename from sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/SqlRuleAction.java rename to sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/SqlRuleActionImpl.java index fe3c87073078e..c4ab9eeb0b9eb 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/SqlRuleAction.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/SqlRuleActionImpl.java @@ -19,7 +19,7 @@ @JsonTypeName("SqlRuleAction") @JacksonXmlRootElement(localName = "SqlRuleAction") @Fluent -public final class SqlRuleAction extends RuleAction { +public final class SqlRuleActionImpl extends RuleActionImpl { /* * The sqlExpression property. */ @@ -38,10 +38,10 @@ public final class SqlRuleAction extends RuleAction { private static final class ParametersWrapper { @JacksonXmlProperty(localName = "KeyValueOfstringanyType") - private final List items; + private final List items; @JsonCreator - private ParametersWrapper(@JacksonXmlProperty(localName = "KeyValueOfstringanyType") List items) { + private ParametersWrapper(@JacksonXmlProperty(localName = "KeyValueOfstringanyType") List items) { this.items = items; } } @@ -77,7 +77,7 @@ public String getSqlExpression() { * @param sqlExpression the sqlExpression value to set. * @return the SqlRuleAction object itself. */ - public SqlRuleAction setSqlExpression(String sqlExpression) { + public SqlRuleActionImpl setSqlExpression(String sqlExpression) { this.sqlExpression = sqlExpression; return this; } @@ -97,7 +97,7 @@ public String getCompatibilityLevel() { * @param compatibilityLevel the compatibilityLevel value to set. * @return the SqlRuleAction object itself. */ - public SqlRuleAction setCompatibilityLevel(String compatibilityLevel) { + public SqlRuleActionImpl setCompatibilityLevel(String compatibilityLevel) { this.compatibilityLevel = compatibilityLevel; return this; } @@ -107,9 +107,9 @@ public SqlRuleAction setCompatibilityLevel(String compatibilityLevel) { * * @return the parameters value. */ - public List getParameters() { + public List getParameters() { if (this.parameters == null) { - this.parameters = new ParametersWrapper(new ArrayList()); + this.parameters = new ParametersWrapper(new ArrayList()); } return this.parameters.items; } @@ -120,7 +120,7 @@ public List getParameters() { * @param parameters the parameters value to set. * @return the SqlRuleAction object itself. */ - public SqlRuleAction setParameters(List parameters) { + public SqlRuleActionImpl setParameters(List parameters) { this.parameters = new ParametersWrapper(parameters); return this; } @@ -140,7 +140,7 @@ public Boolean isRequiresPreprocessing() { * @param requiresPreprocessing the requiresPreprocessing value to set. * @return the SqlRuleAction object itself. */ - public SqlRuleAction setRequiresPreprocessing(Boolean requiresPreprocessing) { + public SqlRuleActionImpl setRequiresPreprocessing(Boolean requiresPreprocessing) { this.requiresPreprocessing = requiresPreprocessing; return this; } diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/TrueFilter.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/TrueFilterImpl.java similarity index 92% rename from sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/TrueFilter.java rename to sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/TrueFilterImpl.java index d28423a2c771f..92fa8bbcb7677 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/TrueFilter.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/implementation/models/TrueFilterImpl.java @@ -14,5 +14,5 @@ @JsonTypeName("TrueFilter") @JacksonXmlRootElement(localName = "TrueFilter") @Immutable -public final class TrueFilter extends SqlFilter { +public final class TrueFilterImpl extends SqlFilterImpl { } diff --git a/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationAsyncClientIntegrationTest.java b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationAsyncClientIntegrationTest.java index 86b97304a0581..21d0678f55981 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationAsyncClientIntegrationTest.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationAsyncClientIntegrationTest.java @@ -15,11 +15,14 @@ import com.azure.messaging.servicebus.administration.models.CreateQueueOptions; import com.azure.messaging.servicebus.administration.models.CreateSubscriptionOptions; import com.azure.messaging.servicebus.administration.models.CreateTopicOptions; +import com.azure.messaging.servicebus.administration.models.EmptyRuleAction; import com.azure.messaging.servicebus.administration.models.NamespaceType; import com.azure.messaging.servicebus.administration.models.QueueRuntimeInfo; +import com.azure.messaging.servicebus.administration.models.RuleProperties; import com.azure.messaging.servicebus.administration.models.SubscriptionRuntimeInfo; import com.azure.messaging.servicebus.administration.models.TopicProperties; import com.azure.messaging.servicebus.administration.models.TopicRuntimeInfo; +import com.azure.messaging.servicebus.administration.models.TrueRuleFilter; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; @@ -35,6 +38,7 @@ import static com.azure.messaging.servicebus.TestUtils.getEntityName; import static com.azure.messaging.servicebus.TestUtils.getSessionSubscriptionBaseName; +import static com.azure.messaging.servicebus.TestUtils.getSubscriptionBaseName; import static com.azure.messaging.servicebus.TestUtils.getTopicBaseName; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -356,6 +360,39 @@ void getQueueRuntimeInfo(HttpClient httpClient) { .verifyComplete(); } + @ParameterizedTest + @MethodSource("createHttpClients") + void getRule(HttpClient httpClient) { + // Arrange + final ServiceBusAdministrationAsyncClient client = createClient(httpClient); + + // There is a single default rule created. + final String ruleName = "$Default"; + final String topicName = interceptorManager.isPlaybackMode() + ? "topic-13" + : getEntityName(getTopicBaseName(), 13); + final String subscriptionName = interceptorManager.isPlaybackMode() + ? "subscription" + : getSubscriptionBaseName(); + + // Act & Assert + StepVerifier.create(client.getRuleWithResponse(topicName, subscriptionName, ruleName)) + .assertNext(response -> { + assertEquals(200, response.getStatusCode()); + + final RuleProperties contents = response.getValue(); + + assertNotNull(contents); + assertEquals(ruleName, contents.getName()); + assertNotNull(contents.getFilter()); + assertTrue(contents.getFilter() instanceof TrueRuleFilter); + + assertNotNull(contents.getAction()); + assertTrue(contents.getAction() instanceof EmptyRuleAction); + }) + .verifyComplete(); + } + @ParameterizedTest @MethodSource("createHttpClients") void getSubscription(HttpClient httpClient) { @@ -570,6 +607,34 @@ void getTopicRuntimeInfo(HttpClient httpClient) { .verifyComplete(); } + @ParameterizedTest + @MethodSource("createHttpClients") + void listRules(HttpClient httpClient) { + // Arrange + final ServiceBusAdministrationAsyncClient client = createClient(httpClient); + + // There is a single default rule created. + final String ruleName = "$Default"; + final String topicName = interceptorManager.isPlaybackMode() + ? "topic-13" + : getEntityName(getTopicBaseName(), 13); + final String subscriptionName = interceptorManager.isPlaybackMode() + ? "subscription" + : getSubscriptionBaseName(); + + // Act & Assert + StepVerifier.create(client.listRules(topicName, subscriptionName)) + .assertNext(response -> { + assertEquals(ruleName, response.getName()); + assertNotNull(response.getFilter()); + assertTrue(response.getFilter() instanceof TrueRuleFilter); + + assertNotNull(response.getAction()); + assertTrue(response.getAction() instanceof EmptyRuleAction); + }) + .verifyComplete(); + } + @ParameterizedTest @MethodSource("createHttpClients") void listQueues(HttpClient httpClient) { diff --git a/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/models/CorrelationRuleFilterTest.java b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/models/CorrelationRuleFilterTest.java new file mode 100644 index 0000000000000..2537a8e5f5a63 --- /dev/null +++ b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/models/CorrelationRuleFilterTest.java @@ -0,0 +1,141 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.messaging.servicebus.administration.models; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests for {@link CorrelationRuleFilter}. + */ +class CorrelationRuleFilterTest { + @Test + void constructorDefault() { + // Arrange + final String expectedToString = "CorrelationRuleFilter: "; + + // Act + final CorrelationRuleFilter actual = new CorrelationRuleFilter(); + + // Assert + assertNull(actual.getCorrelationId()); + assertNull(actual.getContentType()); + assertNull(actual.getLabel()); + assertNull(actual.getMessageId()); + assertNull(actual.getReplyTo()); + assertNull(actual.getReplyToSessionId()); + assertNull(actual.getSessionId()); + assertNull(actual.getTo()); + + final String toString = actual.toString(); + assertNotNull(toString); + assertEquals(expectedToString, toString); + } + + @Test + void constructor() { + // Arrange + final String expected = "some-id"; + + // Act + final CorrelationRuleFilter actual = new CorrelationRuleFilter(expected); + + // Assert + assertEquals(expected, actual.getCorrelationId()); + assertNull(actual.getContentType()); + assertNull(actual.getLabel()); + assertNull(actual.getMessageId()); + assertNull(actual.getReplyTo()); + assertNull(actual.getReplyToSessionId()); + assertNull(actual.getSessionId()); + assertNull(actual.getTo()); + } + + @Test + void setValues() { + // Arrange + final String correlationId = "some-id"; + final String contentType = "some-content-type"; + final String label = "some-label"; + final String messageId = "some-message-id"; + final String replyId = "some-reply-id"; + final String replyIdSessionId = "some-reply-session-id"; + final String sessionId = "some-session-id"; + final String to = "some-to"; + + final CorrelationRuleFilter actual = new CorrelationRuleFilter("fake"); + + // Act + actual.setCorrelationId(correlationId); + actual.setContentType(contentType); + actual.setLabel(label); + actual.setMessageId(messageId); + actual.setReplyTo(replyId); + actual.setReplyToSessionId(replyIdSessionId); + actual.setSessionId(sessionId); + actual.setTo(to); + + // Assert + assertEquals(correlationId, actual.getCorrelationId()); + assertEquals(contentType, actual.getContentType()); + assertEquals(label, actual.getLabel()); + assertEquals(messageId, actual.getMessageId()); + assertEquals(replyId, actual.getReplyTo()); + assertEquals(replyIdSessionId, actual.getReplyToSessionId()); + assertEquals(sessionId, actual.getSessionId()); + assertEquals(to, actual.getTo()); + + final String toString = actual.toString(); + assertNotNull(toString); + assertFalse(toString.isEmpty()); + assertTrue(toString.contains(correlationId)); + assertTrue(toString.contains(contentType)); + assertTrue(toString.contains(label)); + assertTrue(toString.contains(messageId)); + assertTrue(toString.contains(replyId)); + assertTrue(toString.contains(replyIdSessionId)); + assertTrue(toString.contains(sessionId)); + assertTrue(toString.contains(to)); + } + + @Test + void setProperties() { + // Arrange + final String key1 = "some-key1"; + final String value1 = "some-value1"; + final String key2 = "some-key2"; + final String value2 = "some-value2"; + final String correlationId = "some-id"; + final String contentType = "some-content-type"; + final String label = "some-label"; + + final CorrelationRuleFilter actual = new CorrelationRuleFilter("fake"); + + // Act + actual.setCorrelationId(correlationId); + actual.setContentType(contentType); + actual.setLabel(label); + + actual.getProperties().put(key1, value1); + actual.getProperties().put(key2, value2); + + // Assert + assertEquals(correlationId, actual.getCorrelationId()); + assertEquals(contentType, actual.getContentType()); + assertEquals(label, actual.getLabel()); + + final String toString = actual.toString(); + assertNotNull(toString); + assertFalse(toString.isEmpty()); + assertTrue(toString.contains(key1)); + assertTrue(toString.contains(value1)); + assertTrue(toString.contains(key2)); + assertTrue(toString.contains(value2)); + } +} diff --git a/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/implementation/ServiceBusManagementSerializerTest.java b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/implementation/ServiceBusManagementSerializerTest.java index ed842ee3e1034..f91baa2e6fe3d 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/implementation/ServiceBusManagementSerializerTest.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/implementation/ServiceBusManagementSerializerTest.java @@ -12,6 +12,10 @@ import com.azure.messaging.servicebus.administration.models.QueueProperties; import com.azure.messaging.servicebus.administration.models.QueueRuntimeInfo; import com.azure.messaging.servicebus.administration.models.SubscriptionRuntimeInfo; +import com.azure.messaging.servicebus.implementation.models.CorrelationFilterImpl; +import com.azure.messaging.servicebus.implementation.models.EmptyRuleActionImpl; +import com.azure.messaging.servicebus.implementation.models.FalseFilterImpl; +import com.azure.messaging.servicebus.implementation.models.KeyValueImpl; import com.azure.messaging.servicebus.implementation.models.MessageCountDetails; import com.azure.messaging.servicebus.implementation.models.NamespacePropertiesEntry; import com.azure.messaging.servicebus.implementation.models.QueueDescription; @@ -20,10 +24,17 @@ import com.azure.messaging.servicebus.implementation.models.QueueDescriptionFeed; import com.azure.messaging.servicebus.implementation.models.ResponseAuthor; import com.azure.messaging.servicebus.implementation.models.ResponseLink; +import com.azure.messaging.servicebus.implementation.models.RuleDescription; +import com.azure.messaging.servicebus.implementation.models.RuleDescriptionEntry; +import com.azure.messaging.servicebus.implementation.models.RuleDescriptionEntryContent; +import com.azure.messaging.servicebus.implementation.models.RuleDescriptionFeed; +import com.azure.messaging.servicebus.implementation.models.SqlFilterImpl; +import com.azure.messaging.servicebus.implementation.models.SqlRuleActionImpl; import com.azure.messaging.servicebus.implementation.models.SubscriptionDescription; import com.azure.messaging.servicebus.implementation.models.SubscriptionDescriptionEntry; import com.azure.messaging.servicebus.implementation.models.SubscriptionDescriptionEntryContent; import com.azure.messaging.servicebus.implementation.models.SubscriptionDescriptionFeed; +import com.azure.messaging.servicebus.implementation.models.TrueFilterImpl; import org.junit.jupiter.api.Test; import java.io.File; @@ -41,9 +52,12 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -478,6 +492,171 @@ void deserializeSubscriptionDescriptionFeed() throws IOException { } } + /** + * Verify we can deserialize XML from a GET rule. + */ + @Test + void deserializeSqlRule() throws IOException { + // Arrange + final String contents = getContents("SqlRuleFilter.xml"); + final RuleDescription expectedRule = new RuleDescription() + .setName("foo") + .setCreatedAt(OffsetDateTime.parse("2020-08-28T04:32:20.9387321Z")) + .setAction(new EmptyRuleActionImpl()) + .setFilter(new SqlFilterImpl() + .setCompatibilityLevel("20") + .setSqlExpression("type = \"TestType\"")); + final RuleDescriptionEntry expected = new RuleDescriptionEntry() + .setId("sb://sb-java-conniey-3.servicebus.windows.net/topic-10/Subscriptions/subscription/Rules/foo?api-version=2017-04&enrich=false") + .setPublished(OffsetDateTime.parse("2020-08-28T04:32:20Z")) + .setUpdated(OffsetDateTime.parse("2020-08-28T04:34:20Z")) + .setContent(new RuleDescriptionEntryContent() + .setRuleDescription(expectedRule) + .setType("application/xml")); + + // Act + final RuleDescriptionEntry actual = serializer.deserialize(contents, RuleDescriptionEntry.class); + + // Assert + assertRuleEntryEquals(expected, actual); + } + + /** + * Verify we can deserialize XML from a GET rule that includes an action. + */ + @Test + void deserializeSqlRuleWithAction() throws IOException { + // Arrange + final String contents = getContents("SqlRuleFilterWithAction.xml"); + final RuleDescription expectedRule = new RuleDescription() + .setName("foo") + .setCreatedAt(OffsetDateTime.parse("2020-08-28T04:51:24.9967451Z")) + .setAction(new SqlRuleActionImpl() + .setCompatibilityLevel("20") + .setSqlExpression("set FilterTag = 'true'")) + .setFilter(new SqlFilterImpl() + .setCompatibilityLevel("20") + .setSqlExpression("type = \"TestType\"")); + final RuleDescriptionEntry expected = new RuleDescriptionEntry() + .setId("https://sb-java-conniey-3.servicebus.windows.net/topic-10/Subscriptions/subscription/Rules/foo?api-version=2017-04") + .setPublished(OffsetDateTime.parse("2020-08-28T04:51:24Z")) + .setUpdated(OffsetDateTime.parse("2020-08-28T04:54:24Z")) + .setContent(new RuleDescriptionEntryContent() + .setRuleDescription(expectedRule) + .setType("application/xml")); + + // Act + final RuleDescriptionEntry actual = serializer.deserialize(contents, RuleDescriptionEntry.class); + + // Assert + assertRuleEntryEquals(expected, actual); + } + + /** + * Verify we can deserialize XML from a GET correlation filter rule that includes an action. + */ + @Test + void deserializeCorrelationFilterRule() throws IOException { + // Arrange + final String contents = getContents("CorrelationRuleFilter.xml"); + final RuleDescription expectedRule = new RuleDescription() + .setName("correlation-test") + .setCreatedAt(OffsetDateTime.parse("2020-08-28T04:32:50.7697024Z")) + .setAction(new EmptyRuleActionImpl()) + .setFilter(new CorrelationFilterImpl() + .setLabel("matching-label")); + final RuleDescriptionEntry expected = new RuleDescriptionEntry() + .setId("sb://sb-java-conniey-3.servicebus.windows.net/topic-10/Subscriptions/subscription/Rules/correl?api-version=2017-04&enrich=false") + .setPublished(OffsetDateTime.parse("2020-08-28T04:32:50Z")) + .setUpdated(OffsetDateTime.parse("2020-08-28T04:34:50Z")) + .setContent(new RuleDescriptionEntryContent() + .setRuleDescription(expectedRule) + .setType("application/xml")); + + // Act + final RuleDescriptionEntry actual = serializer.deserialize(contents, RuleDescriptionEntry.class); + + // Assert + assertRuleEntryEquals(expected, actual); + } + + /** + * Verify we can deserialize XML from a GET rule that includes an action. + */ + @Test + void deserializeRulesFeed() throws IOException { + // Arrange + final String contents = getContents("RuleDescriptionFeed.xml"); + + final RuleDescription defaultRule = new RuleDescription() + .setName("$Default") + .setCreatedAt(OffsetDateTime.parse("2020-08-12T18:48:00.1005312Z")) + .setAction(new EmptyRuleActionImpl()) + .setFilter(new TrueFilterImpl().setCompatibilityLevel("20").setSqlExpression("1=1")); + final RuleDescriptionEntry defaultRuleEntry = new RuleDescriptionEntry() + .setId("https://sb-java-conniey-3.servicebus.windows.net/topic-10/Subscriptions/subscription/rules/$Default?api-version=2017-04") + .setPublished(OffsetDateTime.parse("2020-08-12T18:48:00Z")) + .setUpdated(OffsetDateTime.parse("2020-08-12T18:48:00Z")) + .setContent(new RuleDescriptionEntryContent() + .setRuleDescription(defaultRule) + .setType("application/xml")); + + final RuleDescription correlation = new RuleDescription() + .setName("correl") + .setCreatedAt(OffsetDateTime.parse("2020-08-28T04:32:50.7697024Z")) + .setAction(new EmptyRuleActionImpl()) + .setFilter(new CorrelationFilterImpl() + .setLabel("matching-label")); + final RuleDescriptionEntry correlationEntry = new RuleDescriptionEntry() + .setId("https://sb-java-conniey-3.servicebus.windows.net/topic-10/Subscriptions/subscription/rules/correl?api-version=2017-04") + .setPublished(OffsetDateTime.parse("2020-08-28T04:32:50Z")) + .setUpdated(OffsetDateTime.parse("2020-08-28T04:32:50Z")) + .setContent(new RuleDescriptionEntryContent() + .setRuleDescription(correlation) + .setType("application/xml")); + + final RuleDescription sqlRule = new RuleDescription() + .setName("foo") + .setCreatedAt(OffsetDateTime.parse("2020-08-28T04:51:24.9967451Z")) + .setAction(new SqlRuleActionImpl() + .setCompatibilityLevel("20") + .setSqlExpression("set FilterTag = 'true'")) + .setFilter(new SqlFilterImpl() + .setCompatibilityLevel("20") + .setSqlExpression("type = \"TestType\"")); + final RuleDescriptionEntry sqlRuleEntry = new RuleDescriptionEntry() + .setId("https://sb-java-conniey-3.servicebus.windows.net/topic-10/Subscriptions/subscription/rules/foo?api-version=2017-04") + .setPublished(OffsetDateTime.parse("2020-08-28T04:32:20Z")) + .setUpdated(OffsetDateTime.parse("2020-08-28T04:32:20Z")) + .setContent(new RuleDescriptionEntryContent() + .setRuleDescription(sqlRule) + .setType("application/xml")); + + final List expectedEntries = Arrays.asList(defaultRuleEntry, correlationEntry, sqlRuleEntry); + final RuleDescriptionFeed expected = new RuleDescriptionFeed() + .setEntry(expectedEntries) + .setId("https://sb-java-conniey-3.servicebus.windows.net/topic-10/Subscriptions/subscription/rules?api-version=2017-04&enrich=false&$skip=0&$top=100") + .setUpdated(OffsetDateTime.parse("2020-08-28T14:59:16Z")); + + // Act + final RuleDescriptionFeed actual = serializer.deserialize(contents, RuleDescriptionFeed.class); + + // Assert + assertNotNull(actual); + assertEquals(expected.getId(), actual.getId()); + + final List actualEntries = actual.getEntry(); + assertNotNull(actualEntries); + assertEquals(expectedEntries.size(), actualEntries.size()); + + for (int i = 0; i < expected.getEntry().size(); i++) { + final RuleDescriptionEntry expectedRule = expectedEntries.get(i); + final RuleDescriptionEntry actualRule = actualEntries.get(i); + + assertRuleEntryEquals(expectedRule, actualRule); + } + } + /** * Given a file name, gets the corresponding resource and its contents as a string. * @@ -538,6 +717,111 @@ private static void assertSubscriptionEquals(SubscriptionDescription expected, E assertEquals(expectedStatus, actual.getStatus()); } + private static void assertRuleEntryEquals(RuleDescriptionEntry expected, RuleDescriptionEntry actual) { + if (expected == null) { + assertNull(actual); + return; + } + + assertNotNull(actual); + assertEquals(expected.getId(), actual.getId()); + + if (expected.getContent() == null) { + assertNull(actual.getContent()); + return; + } + + assertNotNull(actual.getContent()); + assertEquals(expected.getContent().getType(), actual.getContent().getType()); + + final RuleDescription expectedRule = expected.getContent().getRuleDescription(); + final RuleDescription actualRule = actual.getContent().getRuleDescription(); + assertNotNull(actualRule); + assertRuleEquals(expectedRule, actualRule); + } + + private static void assertRuleEquals(RuleDescription expected, RuleDescription actual) { + if (expected == null) { + assertNull(actual); + return; + } + + assertNotNull(actual); + assertEquals(expected.getName(), actual.getName()); + + // Rule action assertions. + if (expected.getAction() instanceof EmptyRuleActionImpl) { + assertTrue(actual.getAction() instanceof EmptyRuleActionImpl); + } else if (expected.getAction() instanceof SqlRuleActionImpl) { + assertTrue(actual.getAction() instanceof SqlRuleActionImpl); + + final SqlRuleActionImpl expectedAction = (SqlRuleActionImpl) expected.getAction(); + final SqlRuleActionImpl actualAction = (SqlRuleActionImpl) actual.getAction(); + + assertEquals(expectedAction.getCompatibilityLevel(), actualAction.getCompatibilityLevel()); + assertEquals(expectedAction.getSqlExpression(), actualAction.getSqlExpression()); + assertEquals(expectedAction.isRequiresPreprocessing(), actualAction.isRequiresPreprocessing()); + + assertParameters(expectedAction.getParameters(), actualAction.getParameters()); + } + + // Rule filter assertions. + if (expected.getFilter() instanceof TrueFilterImpl) { + assertTrue(actual.getFilter() instanceof TrueFilterImpl); + } else if (expected.getFilter() instanceof FalseFilterImpl) { + assertTrue(actual.getFilter() instanceof FalseFilterImpl); + } + + if (expected.getFilter() instanceof SqlFilterImpl) { + assertTrue(actual.getFilter() instanceof SqlFilterImpl); + + final SqlFilterImpl expectedFilter = (SqlFilterImpl) expected.getFilter(); + final SqlFilterImpl actualFilter = (SqlFilterImpl) actual.getFilter(); + + assertEquals(expectedFilter.getCompatibilityLevel(), actualFilter.getCompatibilityLevel()); + assertEquals(expectedFilter.getSqlExpression(), actualFilter.getSqlExpression()); + + assertParameters(expectedFilter.getParameters(), actualFilter.getParameters()); + } else if (expected.getFilter() instanceof CorrelationFilterImpl) { + assertTrue(actual.getFilter() instanceof CorrelationFilterImpl); + + final CorrelationFilterImpl expectedFilter = (CorrelationFilterImpl) expected.getFilter(); + final CorrelationFilterImpl actualFilter = (CorrelationFilterImpl) actual.getFilter(); + + assertEquals(expectedFilter.getCorrelationId(), actualFilter.getCorrelationId()); + assertEquals(expectedFilter.getMessageId(), actualFilter.getMessageId()); + assertEquals(expectedFilter.getTo(), actualFilter.getTo()); + assertEquals(expectedFilter.getReplyTo(), actualFilter.getReplyTo()); + assertEquals(expectedFilter.getReplyToSessionId(), actualFilter.getReplyToSessionId()); + assertEquals(expectedFilter.getSessionId(), actualFilter.getSessionId()); + assertEquals(expectedFilter.getContentType(), actualFilter.getContentType()); + + assertParameters(expectedFilter.getProperties(), actualFilter.getProperties()); + } + } + + private static void assertParameters(List expected, List actual) { + if (expected == null) { + assertNull(actual); + return; + } + + assertNotNull(actual); + assertEquals(expected.size(), actual.size()); + + final Map actualMap = actual.stream() + .collect(Collectors.toMap(KeyValueImpl::getKey, Function.identity())); + + for (KeyValueImpl item : expected) { + final KeyValueImpl removed = actualMap.remove(item.getKey()); + + assertNotNull(removed); + assertEquals(item.getValue(), removed.getValue()); + } + + assertTrue(actualMap.isEmpty()); + } + @SuppressWarnings("unchecked") private static void assertTitle(String expectedTitle, Object responseTitle) { assertTrue(responseTitle instanceof LinkedHashMap); diff --git a/sdk/servicebus/azure-messaging-servicebus/src/test/resources/session-records/getRule.json b/sdk/servicebus/azure-messaging-servicebus/src/test/resources/session-records/getRule.json new file mode 100644 index 0000000000000..a4dc847f3d6b6 --- /dev/null +++ b/sdk/servicebus/azure-messaging-servicebus/src/test/resources/session-records/getRule.json @@ -0,0 +1,22 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://REDACTED.servicebus.windows.net/topic-13/subscriptions/subscription/rules/$Default?enrich=true&api-version=2017-04", + "Headers" : { + "User-Agent" : "azsdk-java-azure-messaging-servicebus/7.0.0-beta.6 (11.0.5; Windows 10; 10.0)" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "Strict-Transport-Security" : "max-age=31536000", + "Server" : "Microsoft-HTTPAPI/2.0", + "ETag" : "637328573900600000", + "retry-after" : "0", + "StatusCode" : "200", + "Body" : "sb://sb-java-conniey-3.servicebus.windows.net/topic-13/subscriptions/subscription/rules/$Default?enrich=true&api-version=2017-04$Default2020-08-12T18:48:00Z2020-08-12T18:48:00Z1=1202020-08-12T18:48:00.3923954Z$Default", + "Date" : "Mon, 31 Aug 2020 22:15:11 GMT", + "Content-Type" : "application/atom+xml;type=entry;charset=utf-8" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/servicebus/azure-messaging-servicebus/src/test/resources/session-records/listRules.json b/sdk/servicebus/azure-messaging-servicebus/src/test/resources/session-records/listRules.json new file mode 100644 index 0000000000000..9f9c4427b805b --- /dev/null +++ b/sdk/servicebus/azure-messaging-servicebus/src/test/resources/session-records/listRules.json @@ -0,0 +1,22 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://REDACTED.servicebus.windows.net/topic-13/subscriptions/subscription/rules?$skip=0&$top=100&api-version=2017-04", + "Headers" : { + "User-Agent" : "azsdk-java-azure-messaging-servicebus/7.0.0-beta.6 (11.0.5; Windows 10; 10.0)" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "Strict-Transport-Security" : "max-age=31536000", + "Server" : "Microsoft-HTTPAPI/2.0", + "ETag" : "637328573900600000", + "retry-after" : "0", + "StatusCode" : "200", + "Body" : "Ruleshttps://sb-java-conniey-3.servicebus.windows.net/topic-13/subscriptions/subscription/rules?$skip=0&$top=100&api-version=2017-042020-08-31T22:15:19Zhttps://sb-java-conniey-3.servicebus.windows.net/topic-13/subscriptions/subscription/rules/$Default?api-version=2017-04$Default2020-08-12T18:48:00Z2020-08-12T18:48:00Z1=1202020-08-12T18:48:00.3923954Z$Default", + "Date" : "Mon, 31 Aug 2020 22:15:19 GMT", + "Content-Type" : "application/atom+xml;type=feed;charset=utf-8" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/servicebus/azure-messaging-servicebus/src/test/resources/xml/CorrelationRuleFilter.xml b/sdk/servicebus/azure-messaging-servicebus/src/test/resources/xml/CorrelationRuleFilter.xml new file mode 100644 index 0000000000000..8b146963356d0 --- /dev/null +++ b/sdk/servicebus/azure-messaging-servicebus/src/test/resources/xml/CorrelationRuleFilter.xml @@ -0,0 +1,18 @@ + + sb://sb-java-conniey-3.servicebus.windows.net/topic-10/Subscriptions/subscription/Rules/correl?api-version=2017-04&enrich=false + correlation-test + 2020-08-28T04:32:50Z + 2020-08-28T04:34:50Z + + + + + + + + + 2020-08-28T04:32:50.7697024Z + correlation-test + + + diff --git a/sdk/servicebus/azure-messaging-servicebus/src/test/resources/xml/RuleDescriptionFeed.xml b/sdk/servicebus/azure-messaging-servicebus/src/test/resources/xml/RuleDescriptionFeed.xml new file mode 100644 index 0000000000000..22655e704392c --- /dev/null +++ b/sdk/servicebus/azure-messaging-servicebus/src/test/resources/xml/RuleDescriptionFeed.xml @@ -0,0 +1,67 @@ + + Rules + https://sb-java-conniey-3.servicebus.windows.net/topic-10/Subscriptions/subscription/rules?api-version=2017-04&enrich=false&$skip=0&$top=100 + 2020-08-28T14:59:16Z + + + https://sb-java-conniey-3.servicebus.windows.net/topic-10/Subscriptions/subscription/rules/$Default?api-version=2017-04 + $Default + 2020-08-12T18:48:00Z + 2020-08-12T18:48:00Z + + + + + 1=1 + 20 + + + 2020-08-12T18:48:00.1005312Z + $Default + + + + + https://sb-java-conniey-3.servicebus.windows.net/topic-10/Subscriptions/subscription/rules/correl?api-version=2017-04 + correl + 2020-08-28T04:32:50Z + 2020-08-28T04:32:50Z + + + + + + + + + 2020-08-28T04:32:50.7697024Z + correl + + + + + https://sb-java-conniey-3.servicebus.windows.net/topic-10/Subscriptions/subscription/rules/foo?api-version=2017-04 + foo + 2020-08-28T04:32:20Z + 2020-08-28T04:32:20Z + + + + + type = "TestType" + 20 + + + set FilterTag = 'true' + 20 + + 2020-08-28T04:32:20.9387321Z + foo + + + + diff --git a/sdk/servicebus/azure-messaging-servicebus/src/test/resources/xml/SqlRuleFilter.xml b/sdk/servicebus/azure-messaging-servicebus/src/test/resources/xml/SqlRuleFilter.xml new file mode 100644 index 0000000000000..0919bf15fcfec --- /dev/null +++ b/sdk/servicebus/azure-messaging-servicebus/src/test/resources/xml/SqlRuleFilter.xml @@ -0,0 +1,19 @@ + + sb://sb-java-conniey-3.servicebus.windows.net/topic-10/Subscriptions/subscription/Rules/foo?api-version=2017-04&enrich=false + foo + 2020-08-28T04:32:20Z + 2020-08-28T04:34:20Z + + + + + type = "TestType" + 20 + + + + 2020-08-28T04:32:20.9387321Z + foo + + + diff --git a/sdk/servicebus/azure-messaging-servicebus/src/test/resources/xml/SqlRuleFilterWithAction.xml b/sdk/servicebus/azure-messaging-servicebus/src/test/resources/xml/SqlRuleFilterWithAction.xml new file mode 100644 index 0000000000000..0517521fd050c --- /dev/null +++ b/sdk/servicebus/azure-messaging-servicebus/src/test/resources/xml/SqlRuleFilterWithAction.xml @@ -0,0 +1,21 @@ + + https://sb-java-conniey-3.servicebus.windows.net/topic-10/Subscriptions/subscription/Rules/foo?api-version=2017-04 + foo + 2020-08-28T04:51:24Z + 2020-08-28T04:54:24Z + + + + + type = "TestType" + 20 + + + set FilterTag = 'true' + 20 + + 2020-08-28T04:51:24.9967451Z + foo + + + diff --git a/sdk/servicebus/azure-messaging-servicebus/swagger/README.md b/sdk/servicebus/azure-messaging-servicebus/swagger/README.md index 84d097dc6a0e2..82e8d7b824eac 100644 --- a/sdk/servicebus/azure-messaging-servicebus/swagger/README.md +++ b/sdk/servicebus/azure-messaging-servicebus/swagger/README.md @@ -31,13 +31,13 @@ input-file: E:\git\conniey\azure-rest-api-specs-pr\specification\servicebus\data java: true output-folder: ..\ generate-client-as-impl: true -namespace: com.azure.messaging.servicebus +namespace: com.azure.messaging.servicebus.administration generate-client-interfaces: false sync-methods: none license-header: MICROSOFT_MIT_SMALL add-context-parameter: true models-subpackage: implementation.models -custom-types: AccessRights,AuthorizationRule,EntityAvailabilityStatus,EntityStatus,MessageCountDetails,NamespaceProperties,MessagingSku +custom-types: AccessRights,AuthorizationRule,EntityStatus,NamespaceProperties,MessagingSku custom-types-subpackage: models context-client-method-parameter: true enable-xml: true