diff --git a/sdk/spring/CHANGELOG.md b/sdk/spring/CHANGELOG.md index cf8fe0e459e1a..fe42302ea5b1e 100644 --- a/sdk/spring/CHANGELOG.md +++ b/sdk/spring/CHANGELOG.md @@ -14,6 +14,11 @@ This section includes changes in `spring-cloud-azure-autoconfigure` module. #### Features Added - Support auto start-up for the auto-configured Service Bus Processor Client by enabling a new property of `spring.cloud.azure.servicebus.processor.auto-startup`. [#29997](https://github.com/Azure/azure-sdk-for-java/issues/29997) +### Spring Cloud Azure Resource Manager + +#### Bugs Fixed +- Fix the Service Bus stream binder cannot automatically create Topic/Subscriptions from consumer. [#30722](https://github.com/Azure/azure-sdk-for-java/pull/30722). + ## 4.3.0 (2022-06-29) - This release is compatible with Spring Boot 2.5.0-2.5.14, 2.6.0-2.6.9, 2.7.0-2.7.1. (Note: 2.5.x (x>14), 2.6.y (y>9) and 2.7.z (z>1) should be supported, but they aren't tested with this release.) - This release is compatible with Spring Cloud 2020.0.3-2020.0.5, 2021.0.0-2021.0.3. (Note: 2020.0.x (x>5) and 2021.0.y (y>3) should be supported, but they aren't tested with this release.) diff --git a/sdk/spring/spring-cloud-azure-resourcemanager/src/main/java/com/azure/spring/cloud/resourcemanager/implementation/crud/ServiceBusTopicSubscriptionCrud.java b/sdk/spring/spring-cloud-azure-resourcemanager/src/main/java/com/azure/spring/cloud/resourcemanager/implementation/crud/ServiceBusTopicSubscriptionCrud.java index fa03fb65c8eb5..9fb00602b6897 100644 --- a/sdk/spring/spring-cloud-azure-resourcemanager/src/main/java/com/azure/spring/cloud/resourcemanager/implementation/crud/ServiceBusTopicSubscriptionCrud.java +++ b/sdk/spring/spring-cloud-azure-resourcemanager/src/main/java/com/azure/spring/cloud/resourcemanager/implementation/crud/ServiceBusTopicSubscriptionCrud.java @@ -6,6 +6,7 @@ import com.azure.core.management.exception.ManagementException; import com.azure.resourcemanager.AzureResourceManager; import com.azure.resourcemanager.servicebus.models.ServiceBusSubscription; +import com.azure.resourcemanager.servicebus.models.Topic; import com.azure.spring.cloud.core.properties.resource.AzureResourceMetadata; import reactor.util.function.Tuple3; import reactor.util.function.Tuples; @@ -16,11 +17,20 @@ public class ServiceBusTopicSubscriptionCrud extends AbstractResourceCrud> { - + private ServiceBusTopicCrud serviceBusTopicCrud; public ServiceBusTopicSubscriptionCrud(AzureResourceManager azureResourceManager, AzureResourceMetadata azureResourceMetadata) { + this(azureResourceManager, azureResourceMetadata, + new ServiceBusTopicCrud(azureResourceManager, azureResourceMetadata)); + } + + ServiceBusTopicSubscriptionCrud(AzureResourceManager azureResourceManager, + AzureResourceMetadata azureResourceMetadata, + ServiceBusTopicCrud serviceBusTopicCrud) { super(azureResourceManager, azureResourceMetadata); + this.serviceBusTopicCrud = serviceBusTopicCrud; } + @Override String getResourceName(Tuple3 key) { return key.getT3(); @@ -34,10 +44,11 @@ String getResourceType() { @Override public ServiceBusSubscription internalGet(Tuple3 subscriptionCoordinate) { try { - return new ServiceBusTopicCrud(this.resourceManager, this.resourceMetadata) - .get(Tuples.of(subscriptionCoordinate.getT1(), subscriptionCoordinate.getT2())) + Topic topic = this.serviceBusTopicCrud + .get(Tuples.of(subscriptionCoordinate.getT1(), subscriptionCoordinate.getT2())); + return topic == null ? null : topic .subscriptions() - .getByName(subscriptionCoordinate.getT2()); + .getByName(subscriptionCoordinate.getT3()); } catch (ManagementException e) { if (e.getResponse().getStatusCode() == RESOURCE_NOT_FOUND) { return null; @@ -49,7 +60,7 @@ public ServiceBusSubscription internalGet(Tuple3 subscri @Override public ServiceBusSubscription internalCreate(Tuple3 subscriptionCoordinate) { - return new ServiceBusTopicCrud(this.resourceManager, this.resourceMetadata) + return this.serviceBusTopicCrud .getOrCreate(Tuples.of(subscriptionCoordinate.getT1(), subscriptionCoordinate.getT2())) .subscriptions() .define(subscriptionCoordinate.getT3()) diff --git a/sdk/spring/spring-cloud-azure-resourcemanager/src/test/java/com/azure/spring/cloud/resourcemanager/implementation/crud/AbstractResourceCrudTests.java b/sdk/spring/spring-cloud-azure-resourcemanager/src/test/java/com/azure/spring/cloud/resourcemanager/implementation/crud/AbstractResourceCrudTests.java index e793c87bad846..85323cf225041 100644 --- a/sdk/spring/spring-cloud-azure-resourcemanager/src/test/java/com/azure/spring/cloud/resourcemanager/implementation/crud/AbstractResourceCrudTests.java +++ b/sdk/spring/spring-cloud-azure-resourcemanager/src/test/java/com/azure/spring/cloud/resourcemanager/implementation/crud/AbstractResourceCrudTests.java @@ -26,7 +26,7 @@ abstract class AbstractResourceCrudTests { abstract void createStubManagementException(); abstract K getKey(); - ManagementException getManagementException(int statusCode, String message) { + ManagementException createManagementException(int statusCode, String message) { HttpResponse response = mock(HttpResponse.class); when(response.getStatusCode()).thenReturn(statusCode); return new ManagementException(message, response); diff --git a/sdk/spring/spring-cloud-azure-resourcemanager/src/test/java/com/azure/spring/cloud/resourcemanager/implementation/crud/EventHubNamespaceCrudTests.java b/sdk/spring/spring-cloud-azure-resourcemanager/src/test/java/com/azure/spring/cloud/resourcemanager/implementation/crud/EventHubNamespaceCrudTests.java index c4c0d365a97ef..c3b78eb440da0 100644 --- a/sdk/spring/spring-cloud-azure-resourcemanager/src/test/java/com/azure/spring/cloud/resourcemanager/implementation/crud/EventHubNamespaceCrudTests.java +++ b/sdk/spring/spring-cloud-azure-resourcemanager/src/test/java/com/azure/spring/cloud/resourcemanager/implementation/crud/EventHubNamespaceCrudTests.java @@ -23,7 +23,7 @@ AbstractResourceCrud getResourceCrud() { void getStubManagementException(int statusCode, String message) { EventHubNamespaces namespaces = mock(EventHubNamespaces.class); when(resourceManager.eventHubNamespaces()).thenReturn(namespaces); - ManagementException exception = getManagementException(statusCode, message); + ManagementException exception = createManagementException(statusCode, message); when(namespaces.getByResourceGroup(resourceMetadata.getResourceGroup(), getKey())) .thenThrow(exception); } @@ -32,7 +32,7 @@ void getStubManagementException(int statusCode, String message) { void createStubManagementException() { EventHubNamespaces namespaces = mock(EventHubNamespaces.class); when(resourceManager.eventHubNamespaces()).thenReturn(namespaces); - ManagementException exception = getManagementException(500, "Create event hubs namespace exception"); + ManagementException exception = createManagementException(500, "Create event hubs namespace exception"); EventHubNamespace.DefinitionStages.Blank define = mock(EventHubNamespace.DefinitionStages.Blank.class); when(namespaces.define(NAMESPACE)).thenReturn(define); diff --git a/sdk/spring/spring-cloud-azure-resourcemanager/src/test/java/com/azure/spring/cloud/resourcemanager/implementation/crud/EventHubsCrudTests.java b/sdk/spring/spring-cloud-azure-resourcemanager/src/test/java/com/azure/spring/cloud/resourcemanager/implementation/crud/EventHubsCrudTests.java index 9ab116ccb9da1..ed7d2083af57c 100644 --- a/sdk/spring/spring-cloud-azure-resourcemanager/src/test/java/com/azure/spring/cloud/resourcemanager/implementation/crud/EventHubsCrudTests.java +++ b/sdk/spring/spring-cloud-azure-resourcemanager/src/test/java/com/azure/spring/cloud/resourcemanager/implementation/crud/EventHubsCrudTests.java @@ -30,7 +30,7 @@ void getStubManagementException(int statusCode, String message) { EventHubNamespace eventHubNamespace = mock(EventHubNamespace.class); when(resourceManager.eventHubNamespaces()).thenReturn(namespaces); - ManagementException exception = getManagementException(statusCode, message); + ManagementException exception = createManagementException(statusCode, message); when(namespaces.getByResourceGroup(resourceMetadata.getResourceGroup(), getKey().getT1())) .thenReturn(eventHubNamespace); @@ -44,7 +44,7 @@ void getStubManagementException(int statusCode, String message) { void createStubManagementException() { EventHubNamespaces namespaces = mock(EventHubNamespaces.class); EventHubNamespace eventHubNamespace = mock(EventHubNamespace.class); - ManagementException exception = getManagementException(500, "Create event hubs namespace exception"); + ManagementException exception = createManagementException(500, "Create event hubs namespace exception"); when(resourceManager.eventHubNamespaces()).thenReturn(namespaces); when(namespaces.getByResourceGroup(resourceMetadata.getResourceGroup(), getKey().getT1())) .thenReturn(eventHubNamespace); diff --git a/sdk/spring/spring-cloud-azure-resourcemanager/src/test/java/com/azure/spring/cloud/resourcemanager/implementation/crud/ServiceBusNamespaceCrudTests.java b/sdk/spring/spring-cloud-azure-resourcemanager/src/test/java/com/azure/spring/cloud/resourcemanager/implementation/crud/ServiceBusNamespaceCrudTests.java index ef019c0ba7cb5..d030e9ec6a500 100644 --- a/sdk/spring/spring-cloud-azure-resourcemanager/src/test/java/com/azure/spring/cloud/resourcemanager/implementation/crud/ServiceBusNamespaceCrudTests.java +++ b/sdk/spring/spring-cloud-azure-resourcemanager/src/test/java/com/azure/spring/cloud/resourcemanager/implementation/crud/ServiceBusNamespaceCrudTests.java @@ -23,7 +23,7 @@ AbstractResourceCrud getResourceCrud() { void getStubManagementException(int statusCode, String message) { ServiceBusNamespaces namespaces = mock(ServiceBusNamespaces.class); when(resourceManager.serviceBusNamespaces()).thenReturn(namespaces); - ManagementException exception = getManagementException(statusCode, message); + ManagementException exception = createManagementException(statusCode, message); when(namespaces.getByResourceGroup(resourceMetadata.getResourceGroup(), getKey())) .thenThrow(exception); } @@ -32,7 +32,7 @@ void getStubManagementException(int statusCode, String message) { void createStubManagementException() { ServiceBusNamespaces namespaces = mock(ServiceBusNamespaces.class); when(resourceManager.serviceBusNamespaces()).thenReturn(namespaces); - ManagementException exception = getManagementException(500, "Create service bus namespace exception"); + ManagementException exception = createManagementException(500, "Create service bus namespace exception"); ServiceBusNamespace.DefinitionStages.Blank define = mock(ServiceBusNamespace.DefinitionStages.Blank.class); when(namespaces.define(NAMESPACE)).thenReturn(define); diff --git a/sdk/spring/spring-cloud-azure-resourcemanager/src/test/java/com/azure/spring/cloud/resourcemanager/implementation/crud/ServiceBusTopicSubscriptionCrudTests.java b/sdk/spring/spring-cloud-azure-resourcemanager/src/test/java/com/azure/spring/cloud/resourcemanager/implementation/crud/ServiceBusTopicSubscriptionCrudTests.java new file mode 100644 index 0000000000000..e9aebede4fe4d --- /dev/null +++ b/sdk/spring/spring-cloud-azure-resourcemanager/src/test/java/com/azure/spring/cloud/resourcemanager/implementation/crud/ServiceBusTopicSubscriptionCrudTests.java @@ -0,0 +1,128 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.resourcemanager.implementation.crud; + +import com.azure.core.management.exception.ManagementException; +import com.azure.resourcemanager.servicebus.models.ServiceBusSubscription; +import com.azure.resourcemanager.servicebus.models.ServiceBusSubscriptions; +import com.azure.resourcemanager.servicebus.models.Topic; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import reactor.util.function.Tuple2; +import reactor.util.function.Tuple3; +import reactor.util.function.Tuples; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class ServiceBusTopicSubscriptionCrudTests extends AbstractResourceCrudTests> { + + private static final String NAMESPACE = "namespace"; + private static final String TOPIC_NAME = "topic"; + private static final String SUBSCRIPTION_NAME = "subscription"; + private ServiceBusTopicCrud topicCrud; + + @BeforeEach + void beforeEach() { + topicCrud = mock(ServiceBusTopicCrud.class); + super.beforeEach(); + } + + @Override + AbstractResourceCrud> getResourceCrud() { + return new ServiceBusTopicSubscriptionCrud(resourceManager, resourceMetadata, this.topicCrud); + } + + @Override + Tuple3 getKey() { + return Tuples.of(NAMESPACE, TOPIC_NAME, SUBSCRIPTION_NAME); + } + + @Override + void getStubManagementException(int statusCode, String message) { + ManagementException exception = createManagementException(statusCode, message); + Topic topic = mock(Topic.class); + ServiceBusSubscriptions serviceBusSubscriptions = mock(ServiceBusSubscriptions.class); + + Tuple3 subscriptionKey = getKey(); + + when(this.topicCrud.get(Tuples.of(subscriptionKey.getT1(), subscriptionKey.getT2()))).thenReturn(topic); + when(topic.subscriptions()).thenReturn(serviceBusSubscriptions); + when(serviceBusSubscriptions.getByName(subscriptionKey.getT3())).thenThrow(exception); + } + + @Override + void createStubManagementException() { + ManagementException exception = createManagementException(500, "Create service bus namespace exception"); + Topic topic = mock(Topic.class); + ServiceBusSubscriptions serviceBusSubscriptions = mock(ServiceBusSubscriptions.class); + ServiceBusSubscription.DefinitionStages.Blank define = mock(ServiceBusSubscription.DefinitionStages.Blank.class); + + Tuple3 subscriptionKey = getKey(); + + when(this.topicCrud.getOrCreate(Tuples.of(subscriptionKey.getT1(), subscriptionKey.getT2()))).thenReturn(topic); + when(topic.subscriptions()).thenReturn(serviceBusSubscriptions); + when(serviceBusSubscriptions.define(SUBSCRIPTION_NAME)).thenReturn(define); + when(define.create()).thenThrow(exception); + } + + @Test + void topicDoesNotExistReturnNull() { + when(topicCrud.get(Tuples.of(NAMESPACE, TOPIC_NAME))).thenReturn(null); + ServiceBusSubscription actualGet = getResourceCrud().get(getKey()); + Assertions.assertNull(actualGet); + } + + @Test + void topicDoesNotExistsShouldReturnNullTopicAndCreateSub() { + Tuple3 subscriptionKey = getKey(); + Tuple2 topicKey = Tuples.of(subscriptionKey.getT1(), subscriptionKey.getT2()); + + Topic topic = mock(Topic.class); + ServiceBusSubscriptions serviceBusSubscriptions = mock(ServiceBusSubscriptions.class); + when(topic.subscriptions()).thenReturn(serviceBusSubscriptions); + + ServiceBusSubscription.DefinitionStages.Blank define = + mock(ServiceBusSubscription.DefinitionStages.Blank.class); + when(serviceBusSubscriptions.define(subscriptionKey.getT3())).thenReturn(define); + ServiceBusSubscription serviceBusSubscription = mock(ServiceBusSubscription.class); + when(define.create()).thenReturn(serviceBusSubscription); + when(topicCrud.get(topicKey)).thenReturn(null); + when(topicCrud.getOrCreate(topicKey)).thenReturn(topic); + + ServiceBusSubscription actualGet = getResourceCrud().get(subscriptionKey); + ServiceBusSubscription actualCreate = getResourceCrud().create(subscriptionKey); + Assertions.assertNull(actualGet); + Assertions.assertNotNull(actualCreate); + Assertions.assertEquals(serviceBusSubscription, actualCreate); + } + + @Test + void topicExistsSubscriptionDoesNotExistShouldReturnNonNullTopicAndCreateSub() { + Tuple3 subscriptionKey = getKey(); + Tuple2 topicKey = Tuples.of(subscriptionKey.getT1(), subscriptionKey.getT2()); + + Topic topic = mock(Topic.class); + ServiceBusSubscriptions serviceBusSubscriptions = mock(ServiceBusSubscriptions.class); + ServiceBusSubscription subscription = mock(ServiceBusSubscription.class); + ServiceBusSubscription.DefinitionStages.Blank define = + mock(ServiceBusSubscription.DefinitionStages.Blank.class); + + when(this.topicCrud.getOrCreate(topicKey)).thenReturn(topic); + when(topic.subscriptions()).thenReturn(serviceBusSubscriptions); + when(serviceBusSubscriptions.getByName(subscriptionKey.getT3())).thenReturn(null); + when(serviceBusSubscriptions.define(subscriptionKey.getT3())).thenReturn(define); + when(define.create()).thenReturn(subscription); + + ServiceBusSubscription actualGet = getResourceCrud().get(subscriptionKey); + ServiceBusSubscription actualCreate = getResourceCrud().create(subscriptionKey); + + Assertions.assertNull(actualGet); + Assertions.assertNotNull(actualCreate); + Assertions.assertEquals(subscription, actualCreate); + } + +}