diff --git a/eng/pipelines/templates/stages/cosmos-sdk-client.yml b/eng/pipelines/templates/stages/cosmos-sdk-client.yml index 81382ae03867..c88b187cabbb 100644 --- a/eng/pipelines/templates/stages/cosmos-sdk-client.yml +++ b/eng/pipelines/templates/stages/cosmos-sdk-client.yml @@ -81,6 +81,9 @@ stages: ServiceDirectory: cosmos Artifacts: ${{ parameters.Artifacts }} AdditionalModules: ${{ parameters.AdditionalModules }} + ACCOUNT_HOST: 'https://localhost:8081/' + ACCOUNT_KEY: 'C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==' + SECONDARY_ACCOUNT_KEY: 'C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==' # Increased timeout to 90 because of cosmos emulator taking 25-30 mins to download emulator # Issue filed to improve download speed: https://github.com/Azure/azure-sdk-for-java/issues/12970 TimeoutInMinutes: 90 @@ -106,7 +109,7 @@ stages: JavaTestVersion: '1.8' ProfileFlag: '-P integration-test-emulator' DisplayName: 'Spring Emulator only Integration Tests' - AdditionalArgs: '-DargLine="-DACCOUNT_HOST=https://localhost:8081/ -DACCOUNT_KEY=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw== -DSECONDARY_ACCOUNT_KEY=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="' + AdditionalArgs: '-DargLine="-DACCOUNT_HOST=https://localhost:8081/ -DACCOUNT_KEY=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw== -DSECONDARY_ACCOUNT_KEY=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw== -DNEW_ACCOUNT_HOST=https://localhost:8081/ -DNEW_ACCOUNT_KEY=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw== -DNEW_SECONDARY_ACCOUNT_KEY=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="' Encryption_Integration_Tests_Java8: OSVmImage: 'windows-2019' JavaTestVersion: '1.8' diff --git a/sdk/cosmos/azure-spring-data-cosmos/CONTRIBUTING.md b/sdk/cosmos/azure-spring-data-cosmos/CONTRIBUTING.md index daad18919bf4..89f375ad5188 100644 --- a/sdk/cosmos/azure-spring-data-cosmos/CONTRIBUTING.md +++ b/sdk/cosmos/azure-spring-data-cosmos/CONTRIBUTING.md @@ -33,15 +33,22 @@ mvn clean install -Dgpg.skip - Click Databases, and then click Azure Cosmos DB to create your database. - Navigate to the database you have created, and click Access keys and copy your URI and access keys for your database. - 2. Set environment variables ACCOUNT_HOST, ACCOUNT_KEY and SECONDARY_ACCOUNT_KEY, where value of them are Cosmos account URI, primary key and secondary key. + 2. Set environment variables ACCOUNT_HOST, ACCOUNT_KEY and SECONDARY_ACCOUNT_KEY, where value of them are Cosmos account URI, primary key and secondary key. + + `azure-spring-data-cosmos` also support multiple database configuration. So set the second group environment variables NEW_ACCOUNT_HOST, NEW_ACCOUNT_KEY and NEW_SECONDARY_ACCOUNT_KEY, the two group environment variables can be same. 3. Run maven command with `integration-test-azure` profile. ```bash set ACCOUNT_HOST=your-cosmos-account-uri set ACCOUNT_KEY=your-cosmos-account-primary-key set SECONDARY_ACCOUNT_KEY=your-cosmos-account-secondary-key + + set NEW_ACCOUNT_HOST=your-cosmos-account-uri + set NEW_ACCOUNT_KEY=your-cosmos-account-primary-key + set NEW_SECONDARY_ACCOUNT_KEY=your-cosmos-account-secondary-key mvnw -P integration-test-azure clean install ``` + - on Emulator Setup Azure Cosmos DB Emulator by following [this instruction](https://docs.microsoft.com/azure/cosmos-db/local-emulator), and set associated environment variables. Then run test with: diff --git a/sdk/cosmos/azure-spring-data-cosmos/README.md b/sdk/cosmos/azure-spring-data-cosmos/README.md index dea72254e765..7165d7f84b5c 100644 --- a/sdk/cosmos/azure-spring-data-cosmos/README.md +++ b/sdk/cosmos/azure-spring-data-cosmos/README.md @@ -334,6 +334,143 @@ public class SampleApplication implements CommandLineRunner { ``` Autowired UserRepository interface, then can do save, delete and find operations. Spring Data Azure Cosmos DB uses the CosmosTemplate to execute the queries behind *find*, *save* methods. You can use the template yourself for more complex queries. +## Support multi-database configuration +The `azure-spring-data-cosmos` support multi-database configuration, here is a sample to config multiple account database + +### Add the dependency +[//]: # ({x-version-update-start;com.azure:azure-spring-data-cosmos;current}) +```xml + + com.azure + azure-spring-data-cosmos + 3.0.0-beta.1 + +``` +[//]: # ({x-version-update-end}) + +### Config application properties +The example uses the `application.properties` file +```properties +azure.cosmos.primary.uri=your-primary-cosmosDb-uri +azure.cosmos.primary.key=your-primary-cosmosDb-key +azure.cosmos.primary.secondaryKey=your-primary-cosmosDb-secondary-key +azure.cosmos.primary.database=your-primary-cosmosDb-dbName +azure.cosmos.primary.populateQueryMetrics=if-populate-query-metrics + +azure.cosmos.secondary.uri=your-secondary-cosmosDb-uri +azure.cosmos.secondary.key=your-secondary-cosmosDb-key +azure.cosmos.secondary.secondaryKey=your-secondary-cosmosDb-secondary-key +azure.cosmos.secondary.database=your-secondary-cosmosDb-dbName +azure.cosmos.secondary.populateQueryMetrics=if-populate-query-metrics +``` + +### Define Entities and Repositories +The [Entity](https://github.com/Azure/azure-sdk-for-java/tree/master/sdk/cosmos/azure-spring-data-cosmos#create-repositories) definition is same as above. +You can put different database entities into different packages. + +### Setup configuration +The `@EnableReactiveCosmosRepositories` or `@EnableCosmosRepositories` support user-define the cosmos template, use `reactiveCosmosTemplateRef` or `cosmosTemplateRef` to config the name of the `ReactiveCosmosTemplate` or `CosmosTemplate` bean to be used with the repositories detected. + +```java +@Configuration +@PropertySource("classpath:application.properties") +public class DatabaseConfiguration extends AbstractCosmosConfiguration { + + @Bean + @ConfigurationProperties(prefix = "azure.cosmos.primary") + public CosmosProperties primaryDataSourceConfiguration() { + return new CosmosProperties(); + } + + @Bean + @ConfigurationProperties(prefix = "azure.cosmos.secondary") + public CosmosProperties secondaryDataSourceConfiguration() { + return new CosmosProperties(); + } + + @EnableReactiveCosmosRepositories(basePackages = "com.azure.cosmos.multidatasource.primarydatasource") + public class PrimaryDataSourceConfiguration { + @Autowired + @Qualifier("primaryDataSourceConfiguration") + CosmosProperties properties; + @Bean + public CosmosConfig cosmosConfig() { + CosmosConfig cosmosConfig = CosmosConfig.builder() + .cosmosClientBuilder(new CosmosClientBuilder() + .key(properties.getKey()).endpoint(properties.getUri())) + .database(properties.getDatabase()) + .enableQueryMetrics(properties.isQueryMetricsEnabled()) + .build(); + return cosmosConfig; + } + + } + + @EnableReactiveCosmosRepositories(basePackages = "com.azure.cosmos.multidatasource.secondarydatasource", reactiveCosmosTemplateRef = "secondaryReactiveCosmosTemplate") + public class SecondaryDataSourceConfiguration { + @Autowired + @Qualifier("secondaryDataSourceConfiguration") + CosmosProperties properties; + @Bean + public ReactiveCosmosTemplate secondaryReactiveCosmosTemplate(MappingCosmosConverter mappingCosmosConverter) { + CosmosConfig cosmosConfig = CosmosConfig.builder() + .cosmosClientBuilder(new CosmosClientBuilder() + .key(properties.getKey()).endpoint(properties.getUri())) + .database(properties.getDatabase()) + .enableQueryMetrics(properties.isQueryMetricsEnabled()) + .build(); + + return new ReactiveCosmosTemplate(new CosmosFactory(cosmosConfig), mappingCosmosConverter, cosmosConfig.getDatabase()); + } + } +} +``` + +### Create an Application class + + +```java +@SpringBootApplication +public class MultiDatasourceApplication implements CommandLineRunner { + + @Autowired + private UserRepository userRepository; + + @Autowired + private BookRepository bookRepository; + + + private final User user = new User("1024", "1024@geek.com", "1k", "Mars"); + private final Book book = new Book("9780792745488", "Zen and the Art of Motorcycle Maintenance", "Robert M. Pirsig"); + + + public static void main(String[] args) { + SpringApplication.run(MultiDatasourceApplication.class, args); + } + + @Override + public void run(String... args) { + final List users = this.userRepository.findByEmailOrName(this.user.getEmail(), this.user.getName()).collectList().block(); + users.forEach(System.out::println); + final Book book = this.bookRepository.findById("9780792745488").block(); + System.out.println(book); + } + + @PostConstruct + public void setup() { + this.userRepository.save(user).block(); + this.bookRepository.save(book).block(); + + } + + @PreDestroy + public void cleanup() { + this.userRepository.deleteAll().block(); + this.bookRepository.deleteAll().block(); + } +} +``` + ## Beta version package Beta version built from `master` branch are available, you can refer to the [instruction](https://github.com/Azure/azure-sdk-for-java/blob/master/CONTRIBUTING.md#nightly-package-builds) to use beta version packages. diff --git a/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/Constants.java b/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/Constants.java index 16d5c1857659..5ab839e21c31 100644 --- a/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/Constants.java +++ b/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/Constants.java @@ -20,7 +20,6 @@ public final class Constants { public static final String COSMOS_MODULE_NAME = "cosmos"; public static final String COSMOS_MODULE_PREFIX = "cosmos"; - public static final String COSMOS_MAPPING_CONTEXT = "cosmosMappingContext"; public static final String USER_AGENT_SUFFIX = "spring-data/"; diff --git a/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/config/CosmosRepositoryConfigurationExtension.java b/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/config/CosmosRepositoryConfigurationExtension.java index 4ae480a3b25e..732dea8fcdbf 100644 --- a/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/config/CosmosRepositoryConfigurationExtension.java +++ b/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/config/CosmosRepositoryConfigurationExtension.java @@ -4,13 +4,12 @@ package com.azure.spring.data.cosmos.repository.config; import com.azure.spring.data.cosmos.Constants; -import com.azure.spring.data.cosmos.core.mapping.CosmosMappingContext; import com.azure.spring.data.cosmos.repository.CosmosRepository; import com.azure.spring.data.cosmos.repository.support.CosmosRepositoryFactoryBean; -import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource; import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport; import org.springframework.data.repository.config.RepositoryConfigurationSource; import org.springframework.data.repository.core.RepositoryMetadata; @@ -57,19 +56,12 @@ protected Collection> getIdentifyingAnnotations() { @Override public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConfigurationSource config) { super.registerBeansForRoot(registry, config); - - if (!registry.containsBeanDefinition(Constants.COSMOS_MAPPING_CONTEXT)) { - final RootBeanDefinition definition = new RootBeanDefinition(CosmosMappingContext.class); - definition.setRole(AbstractBeanDefinition.ROLE_INFRASTRUCTURE); - definition.setSource(config.getSource()); - - registry.registerBeanDefinition(Constants.COSMOS_MAPPING_CONTEXT, definition); - } } @Override - public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSource source) { - super.postProcess(builder, source); + public void postProcess(BeanDefinitionBuilder builder, AnnotationRepositoryConfigurationSource source) { + final AnnotationAttributes attributes = source.getAttributes(); + builder.addPropertyReference("cosmosOperations", attributes.getString("cosmosTemplateRef")); } // Overriding this to provide reactive repository support. diff --git a/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/config/EnableCosmosRepositories.java b/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/config/EnableCosmosRepositories.java index a2a0cc4b62f8..c399f5c1fef9 100644 --- a/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/config/EnableCosmosRepositories.java +++ b/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/config/EnableCosmosRepositories.java @@ -4,6 +4,7 @@ package com.azure.spring.data.cosmos.repository.config; import com.azure.spring.data.cosmos.Constants; +import com.azure.spring.data.cosmos.core.CosmosTemplate; import com.azure.spring.data.cosmos.repository.support.CosmosRepositoryFactoryBean; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Import; @@ -92,5 +93,12 @@ * @return default value is false */ boolean considerNestedRepositories() default false; + + /** + * Configures the name of the {@link CosmosTemplate} bean to be used with the repositories detected. + * + * @return {@literal cosmosTemplate} by default. + */ + String cosmosTemplateRef() default "cosmosTemplate"; } diff --git a/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/config/EnableReactiveCosmosRepositories.java b/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/config/EnableReactiveCosmosRepositories.java index ade7e47fbed2..7ab04879b6e5 100644 --- a/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/config/EnableReactiveCosmosRepositories.java +++ b/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/config/EnableReactiveCosmosRepositories.java @@ -4,6 +4,7 @@ package com.azure.spring.data.cosmos.repository.config; import com.azure.spring.data.cosmos.Constants; +import com.azure.spring.data.cosmos.core.ReactiveCosmosTemplate; import com.azure.spring.data.cosmos.repository.support.ReactiveCosmosRepositoryFactoryBean; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Import; @@ -92,5 +93,12 @@ * @return default value is false */ boolean considerNestedRepositories() default false; + + /** + * Configures the name of the {@link ReactiveCosmosTemplate} bean to be used with the repositories detected. + * + * @return {@literal reactiveCosmosTemplate} by default. + */ + String reactiveCosmosTemplateRef() default "reactiveCosmosTemplate"; } diff --git a/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/config/ReactiveCosmosRepositoryConfigurationExtension.java b/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/config/ReactiveCosmosRepositoryConfigurationExtension.java index c8541b2d9ff8..dfc7327fdcaf 100644 --- a/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/config/ReactiveCosmosRepositoryConfigurationExtension.java +++ b/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/config/ReactiveCosmosRepositoryConfigurationExtension.java @@ -4,13 +4,12 @@ package com.azure.spring.data.cosmos.repository.config; import com.azure.spring.data.cosmos.Constants; -import com.azure.spring.data.cosmos.core.mapping.CosmosMappingContext; import com.azure.spring.data.cosmos.repository.ReactiveCosmosRepository; import com.azure.spring.data.cosmos.repository.support.ReactiveCosmosRepositoryFactoryBean; -import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource; import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport; import org.springframework.data.repository.config.RepositoryConfigurationSource; import org.springframework.data.repository.core.RepositoryMetadata; @@ -57,19 +56,12 @@ protected Collection> getIdentifyingAnnotations() { @Override public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConfigurationSource config) { super.registerBeansForRoot(registry, config); - - if (!registry.containsBeanDefinition(Constants.COSMOS_MAPPING_CONTEXT)) { - final RootBeanDefinition definition = new RootBeanDefinition(CosmosMappingContext.class); - definition.setRole(AbstractBeanDefinition.ROLE_INFRASTRUCTURE); - definition.setSource(config.getSource()); - - registry.registerBeanDefinition(Constants.COSMOS_MAPPING_CONTEXT, definition); - } } @Override - public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSource source) { - super.postProcess(builder, source); + public void postProcess(BeanDefinitionBuilder builder, AnnotationRepositoryConfigurationSource source) { + final AnnotationAttributes attributes = source.getAttributes(); + builder.addPropertyReference("reactiveCosmosOperations", attributes.getString("reactiveCosmosTemplateRef")); } // Overriding this to provide reactive repository support. diff --git a/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/CosmosRepositoryFactory.java b/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/CosmosRepositoryFactory.java index 3eab5d73fc0c..ac495642ec7d 100644 --- a/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/CosmosRepositoryFactory.java +++ b/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/CosmosRepositoryFactory.java @@ -6,7 +6,6 @@ import com.azure.spring.data.cosmos.core.CosmosOperations; import com.azure.spring.data.cosmos.repository.query.CosmosQueryMethod; import com.azure.spring.data.cosmos.repository.query.PartTreeCosmosQuery; -import org.springframework.context.ApplicationContext; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.repository.core.EntityInformation; import org.springframework.data.repository.core.NamedQueries; @@ -27,18 +26,15 @@ */ public class CosmosRepositoryFactory extends RepositoryFactorySupport { - private final ApplicationContext applicationContext; private final CosmosOperations cosmosOperations; /** * Initialization * * @param cosmosOperations for cosmosDb operations - * @param applicationContext for the context */ - public CosmosRepositoryFactory(CosmosOperations cosmosOperations, ApplicationContext applicationContext) { + public CosmosRepositoryFactory(CosmosOperations cosmosOperations) { this.cosmosOperations = cosmosOperations; - this.applicationContext = applicationContext; } @Override @@ -49,7 +45,7 @@ protected Class getRepositoryBaseClass(RepositoryMetadata metadata) { @Override protected Object getTargetRepository(RepositoryInformation information) { final EntityInformation entityInformation = getEntityInformation(information.getDomainType()); - return getTargetRepositoryViaReflection(information, entityInformation, this.applicationContext); + return getTargetRepositoryViaReflection(information, entityInformation, this.cosmosOperations); } @Override diff --git a/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/CosmosRepositoryFactoryBean.java b/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/CosmosRepositoryFactoryBean.java index 93c62e6294e2..0692a3a2c1e8 100644 --- a/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/CosmosRepositoryFactoryBean.java +++ b/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/CosmosRepositoryFactoryBean.java @@ -4,11 +4,7 @@ import com.azure.spring.data.cosmos.core.CosmosOperations; import com.azure.spring.data.cosmos.core.mapping.CosmosMappingContext; -import org.springframework.beans.BeansException; import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; @@ -21,10 +17,8 @@ * configuration. */ public class CosmosRepositoryFactoryBean, S, ID extends Serializable> - extends RepositoryFactoryBeanSupport - implements ApplicationContextAware { + extends RepositoryFactoryBeanSupport { - private ApplicationContext applicationContext; private CosmosOperations operations; private boolean mappingContextConfigured = false; @@ -42,23 +36,17 @@ public CosmosRepositoryFactoryBean(Class repositoryInterface) { * * @param operations for cosmos operations */ - @Autowired public void setCosmosOperations(CosmosOperations operations) { this.operations = operations; } @Override protected final RepositoryFactorySupport createRepositoryFactory() { - return getFactoryInstance(applicationContext); + return getFactoryInstance(); } - protected RepositoryFactorySupport getFactoryInstance(ApplicationContext applicationContext) { - return new CosmosRepositoryFactory(operations, applicationContext); - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = applicationContext; + protected RepositoryFactorySupport getFactoryInstance() { + return new CosmosRepositoryFactory(operations); } @Override diff --git a/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/ReactiveCosmosRepositoryFactory.java b/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/ReactiveCosmosRepositoryFactory.java index f8433d989417..a949964eb463 100644 --- a/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/ReactiveCosmosRepositoryFactory.java +++ b/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/ReactiveCosmosRepositoryFactory.java @@ -5,7 +5,6 @@ import com.azure.spring.data.cosmos.core.ReactiveCosmosOperations; import com.azure.spring.data.cosmos.repository.query.PartTreeReactiveCosmosQuery; import com.azure.spring.data.cosmos.repository.query.ReactiveCosmosQueryMethod; -import org.springframework.context.ApplicationContext; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.repository.core.EntityInformation; import org.springframework.data.repository.core.NamedQueries; @@ -26,18 +25,14 @@ */ public class ReactiveCosmosRepositoryFactory extends ReactiveRepositoryFactorySupport { - private final ApplicationContext applicationContext; private final ReactiveCosmosOperations cosmosOperations; /** * Initialization * * @param cosmosOperations for cosmosDB operations - * @param applicationContext for the context */ - public ReactiveCosmosRepositoryFactory(ReactiveCosmosOperations cosmosOperations, - ApplicationContext applicationContext) { - this.applicationContext = applicationContext; + public ReactiveCosmosRepositoryFactory(ReactiveCosmosOperations cosmosOperations) { this.cosmosOperations = cosmosOperations; } @@ -50,8 +45,7 @@ public EntityInformation getEntityInformation(Class domainType protected Object getTargetRepository(RepositoryInformation information) { final EntityInformation entityInformation = getEntityInformation(information.getDomainType()); - return getTargetRepositoryViaReflection(information, entityInformation, - this.applicationContext); + return getTargetRepositoryViaReflection(information, entityInformation, this.cosmosOperations); } @Override diff --git a/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/ReactiveCosmosRepositoryFactoryBean.java b/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/ReactiveCosmosRepositoryFactoryBean.java index de81d5415b91..4657a9077050 100644 --- a/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/ReactiveCosmosRepositoryFactoryBean.java +++ b/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/ReactiveCosmosRepositoryFactoryBean.java @@ -4,11 +4,7 @@ import com.azure.spring.data.cosmos.core.ReactiveCosmosOperations; import com.azure.spring.data.cosmos.core.mapping.CosmosMappingContext; -import org.springframework.beans.BeansException; import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; @@ -22,10 +18,8 @@ */ public class ReactiveCosmosRepositoryFactoryBean, S, K extends Serializable> - extends RepositoryFactoryBeanSupport - implements ApplicationContextAware { + extends RepositoryFactoryBeanSupport { - private ApplicationContext applicationContext; private ReactiveCosmosOperations cosmosOperations; private boolean mappingContextConfigured = false; @@ -43,23 +37,17 @@ public ReactiveCosmosRepositoryFactoryBean(Class repositoryInterfac * * @param operations contains cosmos operations */ - @Autowired public void setReactiveCosmosOperations(ReactiveCosmosOperations operations) { this.cosmosOperations = operations; } @Override protected final RepositoryFactorySupport createRepositoryFactory() { - return getFactoryInstance(applicationContext); + return getFactoryInstance(); } - protected RepositoryFactorySupport getFactoryInstance(ApplicationContext applicationContext) { - return new ReactiveCosmosRepositoryFactory(cosmosOperations, applicationContext); - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = applicationContext; + protected RepositoryFactorySupport getFactoryInstance() { + return new ReactiveCosmosRepositoryFactory(cosmosOperations); } @Override diff --git a/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/SimpleCosmosRepository.java b/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/SimpleCosmosRepository.java index 0ea68ee18292..41043a602579 100644 --- a/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/SimpleCosmosRepository.java +++ b/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/SimpleCosmosRepository.java @@ -10,7 +10,6 @@ import com.azure.spring.data.cosmos.core.query.CriteriaType; import com.azure.spring.data.cosmos.core.query.DocumentQuery; import com.azure.spring.data.cosmos.repository.CosmosRepository; -import org.springframework.context.ApplicationContext; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; @@ -32,22 +31,6 @@ public class SimpleCosmosRepository implements Cosmo private final CosmosOperations operation; private final CosmosEntityInformation information; - /** - * Initialization - * - * @param metadata for cosmos entity information - * @param applicationContext to get bean of CosmosOperations class - */ - public SimpleCosmosRepository(CosmosEntityInformation metadata, - ApplicationContext applicationContext) { - this.operation = applicationContext.getBean(CosmosOperations.class); - this.information = metadata; - - if (this.information.isAutoCreateContainer()) { - createContainerIfNotExists(); - } - } - /** * Initialization * diff --git a/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/SimpleReactiveCosmosRepository.java b/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/SimpleReactiveCosmosRepository.java index 4359c52c5e52..e48609d5f917 100644 --- a/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/SimpleReactiveCosmosRepository.java +++ b/sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/repository/support/SimpleReactiveCosmosRepository.java @@ -10,7 +10,6 @@ import com.azure.spring.data.cosmos.core.query.DocumentQuery; import com.azure.spring.data.cosmos.repository.ReactiveCosmosRepository; import org.reactivestreams.Publisher; -import org.springframework.context.ApplicationContext; import org.springframework.data.domain.Sort; import org.springframework.lang.NonNull; import org.springframework.util.Assert; @@ -28,22 +27,6 @@ public class SimpleReactiveCosmosRepository implement private final CosmosEntityInformation entityInformation; private final ReactiveCosmosOperations cosmosOperations; - /** - * Initialization with metadata and applicationContext will create container if required - * - * @param metadata for entityInformation - * @param applicationContext for cosmosOperations - */ - public SimpleReactiveCosmosRepository(CosmosEntityInformation metadata, - ApplicationContext applicationContext) { - this.cosmosOperations = applicationContext.getBean(ReactiveCosmosOperations.class); - this.entityInformation = metadata; - - if (this.entityInformation.isAutoCreateContainer()) { - createContainerIfNotExists(); - } - } - /** * Initialization with metadata and reactiveCosmosOperations * diff --git a/sdk/cosmos/azure-spring-data-cosmos/src/samples/java/com/azure/cosmos/multidatasource/DatabaseConfiguration.java b/sdk/cosmos/azure-spring-data-cosmos/src/samples/java/com/azure/cosmos/multidatasource/DatabaseConfiguration.java new file mode 100644 index 000000000000..258e6bd8ff9c --- /dev/null +++ b/sdk/cosmos/azure-spring-data-cosmos/src/samples/java/com/azure/cosmos/multidatasource/DatabaseConfiguration.java @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.cosmos.multidatasource; + +import com.azure.cosmos.CosmosClientBuilder; +import com.azure.cosmos.CosmosProperties; +import com.azure.spring.data.cosmos.CosmosFactory; +import com.azure.spring.data.cosmos.config.AbstractCosmosConfiguration; +import com.azure.spring.data.cosmos.config.CosmosConfig; +import com.azure.spring.data.cosmos.core.ReactiveCosmosTemplate; +import com.azure.spring.data.cosmos.core.convert.MappingCosmosConverter; +import com.azure.spring.data.cosmos.repository.config.EnableReactiveCosmosRepositories; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +/** + * WARNING: MODIFYING THIS FILE WILL REQUIRE CORRESPONDING UPDATES TO README.md FILE. LINE NUMBERS + * ARE USED TO EXTRACT APPROPRIATE CODE SEGMENTS FROM THIS FILE. ADD NEW CODE AT THE BOTTOM TO AVOID CHANGING + * LINE NUMBERS OF EXISTING CODE SAMPLES. + */ +@Configuration +@PropertySource("classpath:application.properties") +public class DatabaseConfiguration extends AbstractCosmosConfiguration { + + @Bean + @ConfigurationProperties(prefix = "azure.cosmos.primary") + public CosmosProperties primaryDataSourceConfiguration() { + return new CosmosProperties(); + } + + @Bean + @ConfigurationProperties(prefix = "azure.cosmos.secondary") + public CosmosProperties secondaryDataSourceConfiguration() { + return new CosmosProperties(); + } + + @EnableReactiveCosmosRepositories(basePackages = "com.azure.cosmos.multidatasource.primarydatasource") + public class PrimaryDataSourceConfiguration { + @Autowired + @Qualifier("primaryDataSourceConfiguration") + CosmosProperties properties; + @Bean + public CosmosConfig cosmosConfig() { + CosmosConfig cosmosConfig = CosmosConfig.builder() + .cosmosClientBuilder(new CosmosClientBuilder() + .key(properties.getKey()).endpoint(properties.getUri())) + .database(properties.getDatabase()) + .enableQueryMetrics(properties.isQueryMetricsEnabled()) + .build(); + return cosmosConfig; + } + + } + + @EnableReactiveCosmosRepositories(basePackages = "com.azure.cosmos.multidatasource.secondarydatasource", reactiveCosmosTemplateRef = "secondaryReactiveCosmosTemplate") + public class SecondaryDataSourceConfiguration { + @Autowired + @Qualifier("secondaryDataSourceConfiguration") + CosmosProperties properties; + @Bean + public ReactiveCosmosTemplate secondaryReactiveCosmosTemplate(MappingCosmosConverter mappingCosmosConverter) { + CosmosConfig cosmosConfig = CosmosConfig.builder() + .cosmosClientBuilder(new CosmosClientBuilder() + .key(properties.getKey()).endpoint(properties.getUri())) + .database(properties.getDatabase()) + .enableQueryMetrics(properties.isQueryMetricsEnabled()) + .build(); + + return new ReactiveCosmosTemplate(new CosmosFactory(cosmosConfig), mappingCosmosConverter, cosmosConfig.getDatabase()); + } + } +} diff --git a/sdk/cosmos/azure-spring-data-cosmos/src/samples/java/com/azure/cosmos/multidatasource/MultiDatasourceApplication.java b/sdk/cosmos/azure-spring-data-cosmos/src/samples/java/com/azure/cosmos/multidatasource/MultiDatasourceApplication.java new file mode 100644 index 000000000000..85e8d4ae484b --- /dev/null +++ b/sdk/cosmos/azure-spring-data-cosmos/src/samples/java/com/azure/cosmos/multidatasource/MultiDatasourceApplication.java @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.cosmos.multidatasource; + +import com.azure.cosmos.multidatasource.primarydatasource.User; +import com.azure.cosmos.multidatasource.primarydatasource.UserRepository; +import com.azure.cosmos.multidatasource.secondarydatasource.Book; +import com.azure.cosmos.multidatasource.secondarydatasource.BookRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.List; + +/** + * WARNING: MODIFYING THIS FILE WILL REQUIRE CORRESPONDING UPDATES TO README.md FILE. LINE NUMBERS + * ARE USED TO EXTRACT APPROPRIATE CODE SEGMENTS FROM THIS FILE. ADD NEW CODE AT THE BOTTOM TO AVOID CHANGING + * LINE NUMBERS OF EXISTING CODE SAMPLES. + */ +@SpringBootApplication +public class MultiDatasourceApplication implements CommandLineRunner { + + @Autowired + private UserRepository userRepository; + + @Autowired + private BookRepository bookRepository; + + + private final User user = new User("1024", "1024@geek.com", "1k", "Mars"); + private final Book book = new Book("9780792745488", "Zen and the Art of Motorcycle Maintenance", "Robert M. Pirsig"); + + + public static void main(String[] args) { + SpringApplication.run(MultiDatasourceApplication.class, args); + } + + @Override + public void run(String... args) { + final List users = this.userRepository.findByEmailOrName(this.user.getEmail(), this.user.getName()).collectList().block(); + users.forEach(System.out::println); + final Book book = this.bookRepository.findById("9780792745488").block(); + System.out.println(book); + } + + @PostConstruct + public void setup() { + this.userRepository.save(user).block(); + this.bookRepository.save(book).block(); + + } + + @PreDestroy + public void cleanup() { + this.userRepository.deleteAll().block(); + this.bookRepository.deleteAll().block(); + } +} diff --git a/sdk/cosmos/azure-spring-data-cosmos/src/samples/java/com/azure/cosmos/multidatasource/primarydatasource/User.java b/sdk/cosmos/azure-spring-data-cosmos/src/samples/java/com/azure/cosmos/multidatasource/primarydatasource/User.java new file mode 100644 index 000000000000..b75ac9a1546d --- /dev/null +++ b/sdk/cosmos/azure-spring-data-cosmos/src/samples/java/com/azure/cosmos/multidatasource/primarydatasource/User.java @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.cosmos.multidatasource.primarydatasource; + +import com.azure.spring.data.cosmos.core.mapping.Document; +import com.azure.spring.data.cosmos.core.mapping.PartitionKey; +import org.springframework.data.annotation.Id; + +/** + * WARNING: MODIFYING THIS FILE WILL REQUIRE CORRESPONDING UPDATES TO README.md FILE. LINE NUMBERS + * ARE USED TO EXTRACT APPROPRIATE CODE SEGMENTS FROM THIS FILE. ADD NEW CODE AT THE BOTTOM TO AVOID CHANGING + * LINE NUMBERS OF EXISTING CODE SAMPLES. + */ + +@Document(container = "users-container", ru = "400") +public class User { + + @Id + private String id; + + private String email; + + @PartitionKey + private String name; + + private String address; + + public User(String id, String email, String name, String address) { + this.id = id; + this.email = email; + this.name = name; + this.address = address; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String toString() { + return String.format("%s: %s %s %s", this.id, this.email, this.name, this.address); + } +} diff --git a/sdk/cosmos/azure-spring-data-cosmos/src/samples/java/com/azure/cosmos/multidatasource/primarydatasource/UserRepository.java b/sdk/cosmos/azure-spring-data-cosmos/src/samples/java/com/azure/cosmos/multidatasource/primarydatasource/UserRepository.java new file mode 100644 index 000000000000..5e64f41369d4 --- /dev/null +++ b/sdk/cosmos/azure-spring-data-cosmos/src/samples/java/com/azure/cosmos/multidatasource/primarydatasource/UserRepository.java @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.cosmos.multidatasource.primarydatasource; + +import com.azure.spring.data.cosmos.repository.ReactiveCosmosRepository; +import reactor.core.publisher.Flux; + +/** + * WARNING: MODIFYING THIS FILE WILL REQUIRE CORRESPONDING UPDATES TO README.md FILE. LINE NUMBERS + * ARE USED TO EXTRACT APPROPRIATE CODE SEGMENTS FROM THIS FILE. ADD NEW CODE AT THE BOTTOM TO AVOID CHANGING + * LINE NUMBERS OF EXISTING CODE SAMPLES. + */ +public interface UserRepository extends ReactiveCosmosRepository { + + Flux findByName(String firstName); + + + Flux findByEmailOrName(String email, String name); + +} diff --git a/sdk/cosmos/azure-spring-data-cosmos/src/samples/java/com/azure/cosmos/multidatasource/secondarydatasource/Book.java b/sdk/cosmos/azure-spring-data-cosmos/src/samples/java/com/azure/cosmos/multidatasource/secondarydatasource/Book.java new file mode 100644 index 000000000000..8f1db812033c --- /dev/null +++ b/sdk/cosmos/azure-spring-data-cosmos/src/samples/java/com/azure/cosmos/multidatasource/secondarydatasource/Book.java @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.cosmos.multidatasource.secondarydatasource; + +import com.azure.spring.data.cosmos.core.mapping.Document; +import org.springframework.data.annotation.Id; + +/** + * WARNING: MODIFYING THIS FILE WILL REQUIRE CORRESPONDING UPDATES TO README.md FILE. LINE NUMBERS + * ARE USED TO EXTRACT APPROPRIATE CODE SEGMENTS FROM THIS FILE. ADD NEW CODE AT THE BOTTOM TO AVOID CHANGING + * LINE NUMBERS OF EXISTING CODE SAMPLES. + */ + +@Document(container = "book-container", ru = "400") +public class Book { + @Id + private String ibsn; + + private String name; + + private String author; + + public Book(String ibsn, String name, String author) { + this.ibsn = ibsn; + this.name = name; + this.author = author; + } + + public String getIbsn() { + return ibsn; + } + + public void setIbsn(String ibsn) { + this.ibsn = ibsn; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } + + @Override + public String toString() { + return String.format("%s: %s %s", this.ibsn, this.name, this.author); + } +} diff --git a/sdk/cosmos/azure-spring-data-cosmos/src/samples/java/com/azure/cosmos/multidatasource/secondarydatasource/BookRepository.java b/sdk/cosmos/azure-spring-data-cosmos/src/samples/java/com/azure/cosmos/multidatasource/secondarydatasource/BookRepository.java new file mode 100644 index 000000000000..80907ac19b83 --- /dev/null +++ b/sdk/cosmos/azure-spring-data-cosmos/src/samples/java/com/azure/cosmos/multidatasource/secondarydatasource/BookRepository.java @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.cosmos.multidatasource.secondarydatasource; + +import com.azure.spring.data.cosmos.repository.ReactiveCosmosRepository; + +/** + * WARNING: MODIFYING THIS FILE WILL REQUIRE CORRESPONDING UPDATES TO README.md FILE. LINE NUMBERS + * ARE USED TO EXTRACT APPROPRIATE CODE SEGMENTS FROM THIS FILE. ADD NEW CODE AT THE BOTTOM TO AVOID CHANGING + * LINE NUMBERS OF EXISTING CODE SAMPLES. + */ +public interface BookRepository extends ReactiveCosmosRepository { +} diff --git a/sdk/cosmos/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/common/TestConstants.java b/sdk/cosmos/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/common/TestConstants.java index 3bcfbecfa638..026b42aaccbf 100644 --- a/sdk/cosmos/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/common/TestConstants.java +++ b/sdk/cosmos/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/common/TestConstants.java @@ -74,6 +74,7 @@ public final class TestConstants { }; public static final String DB_NAME = "testdb"; + public static final String SECONDARY_DB_NAME = "second_testdb"; public static final String FIRST_NAME = "first_name_li"; public static final String LAST_NAME = "last_name_p"; public static final String ID_1 = "id-1"; diff --git a/sdk/cosmos/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/repository/MultiCosmosTemplateIT.java b/sdk/cosmos/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/repository/MultiCosmosTemplateIT.java new file mode 100644 index 000000000000..d852de82373a --- /dev/null +++ b/sdk/cosmos/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/repository/MultiCosmosTemplateIT.java @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.spring.data.cosmos.repository; + +import com.azure.cosmos.models.PartitionKey; +import com.azure.spring.data.cosmos.common.TestConstants; +import com.azure.spring.data.cosmos.core.ReactiveCosmosTemplate; +import com.azure.spring.data.cosmos.domain.Person; +import com.azure.spring.data.cosmos.repository.support.CosmosEntityInformation; +import org.assertj.core.api.Assertions; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import reactor.core.publisher.Mono; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = {TestRepositoryConfig.class, SecondaryTestRepositoryConfig.class}) +public class MultiCosmosTemplateIT { + private static final Person PRIMARY_TEST_PERSON = new Person(TestConstants.ID_1, + TestConstants.FIRST_NAME, + TestConstants.LAST_NAME, TestConstants.HOBBIES, TestConstants.ADDRESSES); + private static final Person SECONDARY_TEST_PERSON = new Person(TestConstants.ID_2, + TestConstants.NEW_FIRST_NAME, + TestConstants.NEW_LAST_NAME, TestConstants.HOBBIES, TestConstants.ADDRESSES); + private static CosmosEntityInformation personInfo; + private static boolean initialized; + @Autowired + @Qualifier("secondaryReactiveCosmosTemplate") + private ReactiveCosmosTemplate secondaryReactiveCosmosTemplate; + @Autowired + @Qualifier("reactiveCosmosTemplate") + private ReactiveCosmosTemplate primaryReactiveCosmosTemplate; + + @Before + public void setUp() throws ClassNotFoundException { + if (!initialized) { + personInfo = new CosmosEntityInformation<>(Person.class); + initialized = true; + } + } + + @After + public void cleanup() { + } + + @AfterClass + public static void afterClassCleanup() { + } + + @Test + public void testPrimaryTemplate() { + primaryReactiveCosmosTemplate.createContainerIfNotExists(personInfo).block(); + primaryReactiveCosmosTemplate.insert(PRIMARY_TEST_PERSON, + new PartitionKey(personInfo.getPartitionKeyFieldValue(PRIMARY_TEST_PERSON))).block(); + final Mono findById = primaryReactiveCosmosTemplate.findById(PRIMARY_TEST_PERSON.getId(), Person.class); + Assertions.assertThat(findById.block().getFirstName()).isEqualTo(TestConstants.FIRST_NAME); + primaryReactiveCosmosTemplate.deleteAll(Person.class.getSimpleName(), Person.class).block(); + primaryReactiveCosmosTemplate.deleteContainer(personInfo.getContainerName()); + } + + @Test + public void testSecondaryTemplate() { + secondaryReactiveCosmosTemplate.createContainerIfNotExists(personInfo).block(); + secondaryReactiveCosmosTemplate.insert(SECONDARY_TEST_PERSON, + new PartitionKey(personInfo.getPartitionKeyFieldValue(SECONDARY_TEST_PERSON))).block(); + final Mono findById = secondaryReactiveCosmosTemplate.findById(SECONDARY_TEST_PERSON.getId(), Person.class); + Assertions.assertThat(findById.block().getFirstName()).isEqualTo(TestConstants.NEW_FIRST_NAME); + secondaryReactiveCosmosTemplate.deleteAll(Person.class.getSimpleName(), Person.class).block(); + secondaryReactiveCosmosTemplate.deleteContainer(personInfo.getContainerName()); + } + +} diff --git a/sdk/cosmos/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/repository/SecondaryTestRepositoryConfig.java b/sdk/cosmos/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/repository/SecondaryTestRepositoryConfig.java new file mode 100644 index 000000000000..dc18fbcb180c --- /dev/null +++ b/sdk/cosmos/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/repository/SecondaryTestRepositoryConfig.java @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.spring.data.cosmos.repository; + +import com.azure.cosmos.CosmosClientBuilder; +import com.azure.spring.data.cosmos.CosmosFactory; +import com.azure.spring.data.cosmos.common.TestConstants; +import com.azure.spring.data.cosmos.config.CosmosConfig; +import com.azure.spring.data.cosmos.core.ReactiveCosmosTemplate; +import com.azure.spring.data.cosmos.core.convert.MappingCosmosConverter; +import com.azure.spring.data.cosmos.repository.config.EnableCosmosRepositories; +import com.azure.spring.data.cosmos.repository.config.EnableReactiveCosmosRepositories; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.util.StringUtils; + +@Configuration +@PropertySource(value = {"classpath:application.properties"}) +@EnableCosmosRepositories +@EnableReactiveCosmosRepositories(reactiveCosmosTemplateRef = "secondaryReactiveCosmosTemplate") +public class SecondaryTestRepositoryConfig { + @Value("${cosmos.secondary.uri:}") + private String cosmosDbUri; + + @Value("${cosmos.secondary.key:}") + private String cosmosDbKey; + + @Value("${cosmos.secondary.database:}") + private String database; + + @Value("${cosmos.secondary.queryMetricsEnabled}") + private boolean queryMetricsEnabled; + + @Bean + public ReactiveCosmosTemplate secondaryReactiveCosmosTemplate(MappingCosmosConverter mappingCosmosConverter) { + final String dbName = StringUtils.hasText(this.database) ? this.database : TestConstants.SECONDARY_DB_NAME; + CosmosConfig config = CosmosConfig.builder() + .cosmosClientBuilder(new CosmosClientBuilder() + .key(cosmosDbKey) + .endpoint(cosmosDbUri) + .contentResponseOnWriteEnabled(true)) + .database(dbName) + .enableQueryMetrics(queryMetricsEnabled) + .build(); + return new ReactiveCosmosTemplate(new CosmosFactory(config), mappingCosmosConverter, config.getDatabase()); + } +} diff --git a/sdk/cosmos/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/repository/support/CosmosRepositoryFactoryUnitTest.java b/sdk/cosmos/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/repository/support/CosmosRepositoryFactoryUnitTest.java index 086c9cdb29f7..47b03637c98a 100644 --- a/sdk/cosmos/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/repository/support/CosmosRepositoryFactoryUnitTest.java +++ b/sdk/cosmos/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/repository/support/CosmosRepositoryFactoryUnitTest.java @@ -8,8 +8,6 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; import org.springframework.data.repository.core.EntityInformation; import static org.junit.Assert.assertTrue; @@ -20,12 +18,9 @@ public class CosmosRepositoryFactoryUnitTest { @Mock CosmosTemplate dbTemplate; - @Autowired - ApplicationContext applicationContext; - @Test public void useMappingCosmosDBEntityInfoIfMappingContextSet() { - final CosmosRepositoryFactory factory = new CosmosRepositoryFactory(dbTemplate, applicationContext); + final CosmosRepositoryFactory factory = new CosmosRepositoryFactory(dbTemplate); final EntityInformation entityInfo = factory.getEntityInformation(Person.class); assertTrue(entityInfo instanceof CosmosEntityInformation); } diff --git a/sdk/cosmos/azure-spring-data-cosmos/src/test/resources/application.properties b/sdk/cosmos/azure-spring-data-cosmos/src/test/resources/application.properties index 5f47ae7f03be..425d26f61755 100644 --- a/sdk/cosmos/azure-spring-data-cosmos/src/test/resources/application.properties +++ b/sdk/cosmos/azure-spring-data-cosmos/src/test/resources/application.properties @@ -10,3 +10,13 @@ perf.acceptance.percentage=10 # Populate query metrics cosmos.queryMetricsEnabled=true + +cosmos.secondary.uri=${NEW_ACCOUNT_HOST} +cosmos.secondary.key=${NEW_ACCOUNT_KEY} +cosmos.secondary.secondaryKey=${NEW_SECONDARY_ACCOUNT_KEY} + +# Populate query metrics +cosmos.secondary.queryMetricsEnabled=true + + + diff --git a/sdk/cosmos/test-resources.json b/sdk/cosmos/test-resources.json index 447dd5729a44..1d6c382d202a 100644 --- a/sdk/cosmos/test-resources.json +++ b/sdk/cosmos/test-resources.json @@ -28,7 +28,9 @@ "variables": { "apiVersion": "2020-04-01", "accountName": "[toLower(parameters('baseName'))]", + "newAccountName": "[toLower(concat(parameters('baseName'), '2'))]", "resourceId": "[resourceId('Microsoft.DocumentDB/databaseAccounts', variables('accountName'))]", + "newResourceId": "[resourceId('Microsoft.DocumentDB/databaseAccounts', variables('newAccountName'))]", "singleRegionConfiguration": [ { "locationName": "East US 2", @@ -79,6 +81,32 @@ "capabilities": [], "ipRules": [] } + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "[variables('apiVersion')]", + "name": "[variables('newAccountName')]", + "location": "[resourceGroup().location]", + "kind": "GlobalDocumentDB", + "properties": { + "publicNetworkAccess": "Enabled", + "enableAutomaticFailover": false, + "enableMultipleWriteLocations": "[parameters('enableMultipleWriteLocations')]", + "isVirtualNetworkFilterEnabled": false, + "virtualNetworkRules": [], + "disableKeyBasedMetadataWriteAccess": false, + "enableFreeTier": false, + "enableAnalyticalStorage": false, + "databaseAccountOfferType": "Standard", + "consistencyPolicy": { + "defaultConsistencyLevel": "[parameters('defaultConsistencyLevel')]", + "maxIntervalInSeconds": 5, + "maxStalenessPrefix": 100 + }, + "locations": "[variables('locationsConfiguration')]", + "capabilities": [], + "ipRules": [] + } } ], "outputs": { @@ -93,6 +121,18 @@ "SECONDARY_ACCOUNT_KEY": { "type": "string", "value": "[listKeys(variables('resourceId'), variables('apiVersion')).secondaryMasterKey]" + }, + "NEW_ACCOUNT_HOST": { + "type": "string", + "value": "[reference(variables('newResourceId'), variables('apiVersion')).documentEndpoint]" + }, + "NEW_ACCOUNT_KEY": { + "type": "string", + "value": "[listKeys(variables('newResourceId'), variables('apiVersion')).primaryMasterKey]" + }, + "NEW_SECONDARY_ACCOUNT_KEY": { + "type": "string", + "value": "[listKeys(variables('newResourceId'), variables('apiVersion')).secondaryMasterKey]" } } }