Implemented reactive API replacing sync API for DocumentDbTemplate (#411)
* Implemented reactive API replacing sync API for DocumentDbTemplate * Updated Sample code Updated examples Use of Cosmos Key Credential Bumped up versions for spring-data sdk, examples, samples * Code review comments. Improvements with APIs, and codacy comments
This commit is contained in:
Родитель
b9476204cc
Коммит
d1304164a1
30
README.md
30
README.md
|
@ -80,6 +80,9 @@ If you are using Maven, add the following dependency.
|
|||
### Setup Configuration
|
||||
Setup configuration class.
|
||||
|
||||
CosmosKeyCredential feature provides capability to rotate keys on the fly. You can switch keys using switchToSecondaryKey().
|
||||
For more information on this, see the Sample Application code.
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
@EnableDocumentDbRepositories
|
||||
|
@ -91,18 +94,29 @@ public class AppConfiguration extends AbstractDocumentDbConfiguration {
|
|||
@Value("${azure.cosmosdb.key}")
|
||||
private String key;
|
||||
|
||||
@Value("${azure.cosmosdb.secondaryKey}")
|
||||
private String secondaryKey;
|
||||
|
||||
@Value("${azure.cosmosdb.database}")
|
||||
private String dbName;
|
||||
|
||||
private CosmosKeyCredential cosmosKeyCredential;
|
||||
|
||||
public DocumentDBConfig getConfig() {
|
||||
return DocumentDBConfig.builder(uri, key, dbName).build();
|
||||
this.cosmosKeyCredential = new CosmosKeyCredential(key);
|
||||
return DocumentDBConfig.builder(uri, this.cosmosKeyCredential, dbName).build();
|
||||
}
|
||||
|
||||
public void switchToSecondaryKey() {
|
||||
this.cosmosKeyCredential.key(secondaryKey);
|
||||
}
|
||||
}
|
||||
```
|
||||
Or if you want to customize your config:
|
||||
```java
|
||||
public DocumentDBConfig getConfig() {
|
||||
DocumentDBConfig dbConfig = DocumentDBConfig.builder(uri, key, dbName).build();
|
||||
this.cosmosKeyCredential = new CosmosKeyCredential(key);
|
||||
DocumentDBConfig dbConfig = DocumentDBConfig.builder(uri, this.cosmosKeyCredential, dbName).build();
|
||||
dbConfig.getConnectionPolicy().setConnectionMode(ConnectionMode.DirectHttps);
|
||||
dbConfig.getConnectionPolicy().setMaxPoolSize(1000);
|
||||
return dbConfig;
|
||||
|
@ -187,6 +201,9 @@ public class SampleApplication implements CommandLineRunner {
|
|||
|
||||
@Autowired
|
||||
private UserRepository repository;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SampleApplication.class, args);
|
||||
|
@ -203,6 +220,15 @@ public class SampleApplication implements CommandLineRunner {
|
|||
final User result = repository.findOne(testUser.getId(), testUser.getLastName);
|
||||
// if emailAddress is mapped to id, then
|
||||
// final User result = respository.findOne(testUser.getEmailAddress(), testUser.getLastName());
|
||||
|
||||
// Switch to secondary key
|
||||
UserRepositoryConfiguration bean =
|
||||
applicationContext.getBean(UserRepositoryConfiguration.class);
|
||||
bean.switchToSecondaryKey();
|
||||
|
||||
// Now repository will use secondary key
|
||||
repository.save(testUser);
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -6,7 +6,7 @@
|
|||
|
||||
<groupId>com.microsoft.azure</groupId>
|
||||
<artifactId>spring-data-cosmosdb</artifactId>
|
||||
<version>2.1.3-SNAPSHOT</version>
|
||||
<version>2.1.7</version>
|
||||
|
||||
<name>Spring Data for Azure Cosmos DB SQL API</name>
|
||||
<description>Spring Data for Azure Cosmos DB SQL API</description>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>com.microsoft.azure</groupId>
|
||||
<artifactId>spring-data-cosmosdb-samples</artifactId>
|
||||
<version>2.0.5-SNAPSHOT</version>
|
||||
<version>2.1.7</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
|||
<dependency>
|
||||
<groupId>com.microsoft.azure</groupId>
|
||||
<artifactId>spring-data-cosmosdb</artifactId>
|
||||
<version>2.1.0</version>
|
||||
<version>2.1.7</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
|
|
@ -28,5 +28,7 @@ public class DocumentDbProperties {
|
|||
|
||||
private String key;
|
||||
|
||||
private String secondaryKey;
|
||||
|
||||
private String database;
|
||||
}
|
||||
|
|
|
@ -15,11 +15,13 @@
|
|||
*/
|
||||
package example.springdata.cosmosdb;
|
||||
|
||||
import com.azure.data.cosmos.CosmosKeyCredential;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.config.AbstractDocumentDbConfiguration;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.config.DocumentDBConfig;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.repository.config.EnableDocumentDbRepositories;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
|
||||
|
@ -33,8 +35,23 @@ public class UserRepositoryConfiguration extends AbstractDocumentDbConfiguration
|
|||
@Autowired
|
||||
private DocumentDbProperties properties;
|
||||
|
||||
@Override
|
||||
public DocumentDBConfig getConfig() {
|
||||
return DocumentDBConfig.builder(properties.getUri(), properties.getKey(), properties.getDatabase()).build();
|
||||
private CosmosKeyCredential cosmosKeyCredential;
|
||||
|
||||
@Bean
|
||||
public DocumentDBConfig documentDBConfig() {
|
||||
this.cosmosKeyCredential = new CosmosKeyCredential(properties.getKey());
|
||||
return DocumentDBConfig.builder(properties.getUri(), cosmosKeyCredential, properties.getDatabase()).build();
|
||||
}
|
||||
|
||||
public void switchToSecondaryKey() {
|
||||
this.cosmosKeyCredential.key(properties.getSecondaryKey());
|
||||
}
|
||||
|
||||
public void switchToPrimaryKey() {
|
||||
this.cosmosKeyCredential.key(properties.getKey());
|
||||
}
|
||||
|
||||
public void switchKey(String key) {
|
||||
this.cosmosKeyCredential.key(key);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,11 +16,13 @@
|
|||
package example.springdata.cosmosdb;
|
||||
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.query.DocumentDbPageRequest;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.exception.DocumentDBAccessException;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
|
@ -50,6 +52,9 @@ public class UserRepositoryIntegrationTest {
|
|||
@Autowired
|
||||
private UserRepository repository;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.repository.deleteAll();
|
||||
|
@ -57,6 +62,11 @@ public class UserRepositoryIntegrationTest {
|
|||
|
||||
@After
|
||||
public void cleanup() {
|
||||
// Switch back to primary key to reset the invalid key
|
||||
// Switch to invalid key
|
||||
final UserRepositoryConfiguration bean =
|
||||
applicationContext.getBean(UserRepositoryConfiguration.class);
|
||||
bean.switchToPrimaryKey();
|
||||
this.repository.deleteAll();
|
||||
}
|
||||
|
||||
|
@ -119,5 +129,57 @@ public class UserRepositoryIntegrationTest {
|
|||
result = resultList.get(0);
|
||||
Assert.isTrue(result.getId().equals(user.getId()), "should be the same Id");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecondaryKeyRotation() {
|
||||
// Switch to secondary key
|
||||
final UserRepositoryConfiguration bean =
|
||||
applicationContext.getBean(UserRepositoryConfiguration.class);
|
||||
bean.switchToSecondaryKey();
|
||||
|
||||
|
||||
final Address address = new Address(POSTAL_CODE, STREET, CITY);
|
||||
final Role creator = new Role(ROLE_CREATOR, COST_CREATOR);
|
||||
final Role contributor = new Role(ROLE_CONTRIBUTOR, COST_CONTRIBUTOR);
|
||||
final User user = new User(ID, EMAIL, NAME, COUNT, address, Arrays.asList(creator, contributor));
|
||||
|
||||
this.repository.save(user);
|
||||
|
||||
// Test for findById
|
||||
final User result = this.repository.findById(ID).get();
|
||||
Assert.notNull(result, "should be exist in database");
|
||||
Assert.isTrue(result.getId().equals(ID), "should be the same id");
|
||||
|
||||
// Test for findByName
|
||||
final List<User> resultList = this.repository.findByName(user.getName());
|
||||
Assert.isTrue(resultList.size() == 1, "should be only one user here");
|
||||
Assert.isTrue(resultList.get(0).getName().equals(user.getName()), "should be same Name");
|
||||
Assert.notNull(result.getRoleList(), "roleList should not be null");
|
||||
Assert.isTrue(result.getRoleList().size() == user.getRoleList().size(), "must be the same list size");
|
||||
}
|
||||
|
||||
@Test(expected = DocumentDBAccessException.class)
|
||||
public void testInvalidSecondaryKey() {
|
||||
final Address address = new Address(POSTAL_CODE, STREET, CITY);
|
||||
final Role creator = new Role(ROLE_CREATOR, COST_CREATOR);
|
||||
final Role contributor = new Role(ROLE_CONTRIBUTOR, COST_CONTRIBUTOR);
|
||||
final User user = new User(ID, EMAIL, NAME, COUNT, address, Arrays.asList(creator, contributor));
|
||||
|
||||
this.repository.save(user);
|
||||
|
||||
// Test for findById
|
||||
final User result = this.repository.findById(ID).get();
|
||||
Assert.notNull(result, "should be exist in database");
|
||||
Assert.isTrue(result.getId().equals(ID), "should be the same id");
|
||||
|
||||
// Switch to invalid key
|
||||
final UserRepositoryConfiguration bean =
|
||||
applicationContext.getBean(UserRepositoryConfiguration.class);
|
||||
bean.switchKey("Invalid key");
|
||||
|
||||
// Test for findByName
|
||||
this.repository.findByName(user.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
<groupId>com.microsoft.azure</groupId>
|
||||
<artifactId>spring-data-cosmosdb-samples</artifactId>
|
||||
<version>2.0.5-SNAPSHOT</version>
|
||||
<version>2.1.7</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<parent>
|
||||
|
|
|
@ -15,6 +15,7 @@ import com.microsoft.azure.spring.data.cosmosdb.config.DocumentDBConfig;
|
|||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
|
@ -48,12 +49,25 @@ public class DocumentDbFactory {
|
|||
final String userAgent = getUserAgentSuffix() + ";" + policy.getUserAgentSuffix();
|
||||
|
||||
policy.setUserAgentSuffix(userAgent);
|
||||
|
||||
// With introduction to com.azure.data.cosmos.CosmosKeyCredential,
|
||||
// we are giving preference to config.getCosmosKeyCredential()
|
||||
if (config.getCosmosKeyCredential() != null &&
|
||||
!StringUtils.isEmpty(config.getCosmosKeyCredential().key())) {
|
||||
return new DocumentClient(config.getUri(), config.getCosmosKeyCredential().key(),
|
||||
policy, config.getConsistencyLevel());
|
||||
}
|
||||
return new DocumentClient(config.getUri(), config.getKey(), policy, config.getConsistencyLevel());
|
||||
}
|
||||
|
||||
private void validateConfig(@NonNull DocumentDBConfig config) {
|
||||
Assert.hasText(config.getUri(), "cosmosdb host url should have text!");
|
||||
Assert.hasText(config.getKey(), "cosmosdb host key should have text!");
|
||||
if (config.getCosmosKeyCredential() == null) {
|
||||
Assert.hasText(config.getKey(), "cosmosdb host key should have text!");
|
||||
} else if (StringUtils.isEmpty(config.getKey())) {
|
||||
Assert.hasText(config.getCosmosKeyCredential().key(),
|
||||
"cosmosdb credential host key should have text!");
|
||||
}
|
||||
Assert.hasText(config.getDatabase(), "cosmosdb database should have text!");
|
||||
Assert.notNull(config.getConnectionPolicy(), "cosmosdb connection policy should not be null!");
|
||||
}
|
||||
|
|
|
@ -9,8 +9,10 @@ package com.microsoft.azure.spring.data.cosmosdb.config;
|
|||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.microsoft.azure.documentdb.DocumentClient;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.Constants;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.CosmosDbFactory;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.DocumentDbFactory;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.DocumentDbTemplate;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.ReactiveCosmosTemplate;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.convert.MappingDocumentDbConverter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
@ -33,12 +35,23 @@ public abstract class AbstractDocumentDbConfiguration extends DocumentDbConfigur
|
|||
return new DocumentDbFactory(config);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CosmosDbFactory cosmosDbFactory(DocumentDBConfig config) {
|
||||
return new CosmosDbFactory(config);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DocumentDbTemplate documentDbTemplate(DocumentDBConfig config) throws ClassNotFoundException {
|
||||
return new DocumentDbTemplate(this.documentDbFactory(config), this.mappingDocumentDbConverter(),
|
||||
return new DocumentDbTemplate(this.cosmosDbFactory(config), this.mappingDocumentDbConverter(),
|
||||
config.getDatabase());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ReactiveCosmosTemplate cosmosDbTemplate(DocumentDBConfig config) throws ClassNotFoundException {
|
||||
return new ReactiveCosmosTemplate(this.cosmosDbFactory(config), this.mappingDocumentDbConverter(),
|
||||
config.getDatabase());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MappingDocumentDbConverter mappingDocumentDbConverter() throws ClassNotFoundException {
|
||||
return new MappingDocumentDbConverter(this.documentDbMappingContext(), objectMapper);
|
||||
|
|
|
@ -6,23 +6,25 @@
|
|||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core;
|
||||
|
||||
import com.microsoft.azure.documentdb.*;
|
||||
import com.microsoft.azure.documentdb.internal.HttpConstants;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.DocumentDbFactory;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.common.CosmosdbUtils;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.config.DocumentDBConfig;
|
||||
import com.azure.data.cosmos.CosmosClient;
|
||||
import com.azure.data.cosmos.CosmosContainerResponse;
|
||||
import com.azure.data.cosmos.CosmosItemProperties;
|
||||
import com.azure.data.cosmos.CosmosItemRequestOptions;
|
||||
import com.azure.data.cosmos.CosmosItemResponse;
|
||||
import com.azure.data.cosmos.FeedOptions;
|
||||
import com.azure.data.cosmos.FeedResponse;
|
||||
import com.azure.data.cosmos.SqlQuerySpec;
|
||||
import com.microsoft.azure.documentdb.DocumentCollection;
|
||||
import com.microsoft.azure.documentdb.PartitionKey;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.CosmosDbFactory;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.convert.MappingDocumentDbConverter;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.generator.CountQueryGenerator;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.generator.FindQuerySpecGenerator;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.query.Criteria;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.query.CriteriaType;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.query.DocumentDbPageRequest;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.query.DocumentQuery;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.exception.DatabaseCreationException;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.exception.DocumentDBAccessException;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.repository.support.DocumentDbEntityInformation;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
@ -33,8 +35,9 @@ import org.springframework.data.domain.Pageable;
|
|||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
|
@ -43,28 +46,24 @@ import java.util.stream.Collectors;
|
|||
|
||||
@Slf4j
|
||||
public class DocumentDbTemplate implements DocumentDbOperations, ApplicationContextAware {
|
||||
private static final String COUNT_VALUE_KEY = "_aggregate";
|
||||
|
||||
@Getter(AccessLevel.PRIVATE)
|
||||
private final DocumentClient documentClient;
|
||||
private final DocumentDbFactory documentDbFactory;
|
||||
private final MappingDocumentDbConverter mappingDocumentDbConverter;
|
||||
private final ReactiveCosmosTemplate reactiveCosmosTemplate;
|
||||
private final String databaseName;
|
||||
|
||||
private Database databaseCache;
|
||||
private List<String> collectionCache;
|
||||
private final CosmosClient cosmosClient;
|
||||
|
||||
public DocumentDbTemplate(DocumentDbFactory documentDbFactory,
|
||||
public DocumentDbTemplate(CosmosDbFactory cosmosDbFactory,
|
||||
MappingDocumentDbConverter mappingDocumentDbConverter,
|
||||
String dbName) {
|
||||
Assert.notNull(documentDbFactory, "DocumentDbFactory must not be null!");
|
||||
Assert.notNull(cosmosDbFactory, "CosmosDbFactory must not be null!");
|
||||
Assert.notNull(mappingDocumentDbConverter, "MappingDocumentDbConverter must not be null!");
|
||||
|
||||
this.databaseName = dbName;
|
||||
this.documentDbFactory = documentDbFactory;
|
||||
this.documentClient = this.documentDbFactory.getDocumentClient();
|
||||
this.mappingDocumentDbConverter = mappingDocumentDbConverter;
|
||||
this.collectionCache = new ArrayList<>();
|
||||
|
||||
this.reactiveCosmosTemplate = new ReactiveCosmosTemplate(cosmosDbFactory, mappingDocumentDbConverter, dbName);
|
||||
this.databaseName = dbName;
|
||||
this.cosmosClient = cosmosDbFactory.getCosmosClient();
|
||||
}
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
|
@ -80,24 +79,30 @@ public class DocumentDbTemplate implements DocumentDbOperations, ApplicationCont
|
|||
Assert.hasText(collectionName, "collectionName should not be null, empty or only whitespaces");
|
||||
Assert.notNull(objectToSave, "objectToSave should not be null");
|
||||
|
||||
final Document document = mappingDocumentDbConverter.writeDoc(objectToSave);
|
||||
final CosmosItemProperties originalItem = mappingDocumentDbConverter.writeCosmosItemProperties(objectToSave);
|
||||
|
||||
log.debug("execute createDocument in database {} collection {}", this.databaseName, collectionName);
|
||||
|
||||
try {
|
||||
final Resource result = getDocumentClient()
|
||||
.createDocument(getCollectionLink(this.databaseName, collectionName), document,
|
||||
getRequestOptions(partitionKey, null), false).getResource();
|
||||
final CosmosItemRequestOptions options = new CosmosItemRequestOptions();
|
||||
options.partitionKey(toCosmosPartitionKey(partitionKey));
|
||||
|
||||
if (result instanceof Document) {
|
||||
final Document documentInserted = (Document) result;
|
||||
@SuppressWarnings("unchecked") final Class<T> domainClass = (Class<T>) objectToSave.getClass();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Class<T> domainClass = (Class<T>) objectToSave.getClass();
|
||||
|
||||
return mappingDocumentDbConverter.read(domainClass, documentInserted);
|
||||
} else {
|
||||
return null;
|
||||
final CosmosItemResponse response = cosmosClient.getDatabase(this.databaseName)
|
||||
.getContainer(collectionName)
|
||||
.createItem(originalItem, options)
|
||||
.onErrorResume(Mono::error)
|
||||
.block();
|
||||
|
||||
if (response == null) {
|
||||
throw new DocumentDBAccessException("Failed to insert item");
|
||||
}
|
||||
} catch (DocumentClientException e) {
|
||||
|
||||
return mappingDocumentDbConverter.read(domainClass, response.properties());
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new DocumentDBAccessException("insert exception", e);
|
||||
}
|
||||
}
|
||||
|
@ -108,37 +113,29 @@ public class DocumentDbTemplate implements DocumentDbOperations, ApplicationCont
|
|||
return findById(getCollectionName(entityClass), id, entityClass);
|
||||
}
|
||||
|
||||
private boolean isIdFieldAsPartitionKey(@NonNull Class<?> domainClass) {
|
||||
@SuppressWarnings("unchecked") final DocumentDbEntityInformation information
|
||||
= new DocumentDbEntityInformation(domainClass);
|
||||
final String partitionKeyName = information.getPartitionKeyFieldName();
|
||||
final String idName = information.getIdField().getName();
|
||||
|
||||
return partitionKeyName != null && partitionKeyName.equals(idName);
|
||||
}
|
||||
|
||||
public <T> T findById(String collectionName, Object id, Class<T> domainClass) {
|
||||
Assert.hasText(collectionName, "collectionName should not be null, empty or only whitespaces");
|
||||
Assert.notNull(domainClass, "entityClass should not be null");
|
||||
assertValidId(id);
|
||||
|
||||
try {
|
||||
final PartitionKey partitionKey = isIdFieldAsPartitionKey(domainClass) ? new PartitionKey(id) : null;
|
||||
final RequestOptions options = getRequestOptions(partitionKey, null);
|
||||
|
||||
final String documentLink = getDocumentLink(this.databaseName, collectionName, id);
|
||||
final Resource document = getDocumentClient().readDocument(documentLink, options).getResource();
|
||||
|
||||
if (document instanceof Document) {
|
||||
return mappingDocumentDbConverter.read(domainClass, (Document) document);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (DocumentClientException e) {
|
||||
if (e.getStatusCode() == HttpConstants.StatusCodes.NOTFOUND) {
|
||||
return null;
|
||||
}
|
||||
final String query = String.format("select * from root where root.id = '%s'", id.toString());
|
||||
final FeedOptions options = new FeedOptions();
|
||||
options.enableCrossPartitionQuery(true);
|
||||
return cosmosClient
|
||||
.getDatabase(databaseName)
|
||||
.getContainer(collectionName)
|
||||
.queryItems(query, options)
|
||||
.flatMap(cosmosItemFeedResponse -> Mono.justOrEmpty(cosmosItemFeedResponse
|
||||
.results()
|
||||
.stream()
|
||||
.map(cosmosItem -> mappingDocumentDbConverter.read(domainClass, cosmosItem))
|
||||
.findFirst()))
|
||||
.onErrorResume(Mono::error)
|
||||
.blockFirst();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new DocumentDBAccessException("findById exception", e);
|
||||
}
|
||||
}
|
||||
|
@ -154,21 +151,23 @@ public class DocumentDbTemplate implements DocumentDbOperations, ApplicationCont
|
|||
Assert.notNull(object, "Upsert object should not be null");
|
||||
|
||||
try {
|
||||
Document originalDoc;
|
||||
|
||||
if (object instanceof Document) {
|
||||
originalDoc = (Document) object;
|
||||
} else {
|
||||
originalDoc = mappingDocumentDbConverter.writeDoc(object);
|
||||
}
|
||||
final CosmosItemProperties originalItem = mappingDocumentDbConverter.writeCosmosItemProperties(object);
|
||||
|
||||
log.debug("execute upsert document in database {} collection {}", this.databaseName, collectionName);
|
||||
|
||||
final String collectionLink = getCollectionSelfLink(collectionName);
|
||||
final RequestOptions options = getRequestOptions(partitionKey, null);
|
||||
final CosmosItemRequestOptions options = new CosmosItemRequestOptions();
|
||||
options.partitionKey(toCosmosPartitionKey(partitionKey));
|
||||
|
||||
getDocumentClient().upsertDocument(collectionLink, originalDoc, options, false);
|
||||
} catch (DocumentClientException ex) {
|
||||
final CosmosItemResponse cosmosItemResponse = cosmosClient.getDatabase(this.databaseName)
|
||||
.getContainer(collectionName)
|
||||
.upsertItem(originalItem, options)
|
||||
.onErrorResume(Mono::error)
|
||||
.block();
|
||||
|
||||
if (cosmosItemResponse == null) {
|
||||
throw new DocumentDBAccessException("Failed to upsert item");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new DocumentDBAccessException("Failed to upsert document to database.", ex);
|
||||
}
|
||||
}
|
||||
|
@ -184,9 +183,11 @@ public class DocumentDbTemplate implements DocumentDbOperations, ApplicationCont
|
|||
Assert.notNull(domainClass, "entityClass should not be null");
|
||||
|
||||
final DocumentQuery query = new DocumentQuery(Criteria.getInstance(CriteriaType.ALL));
|
||||
final List<Document> results = findDocuments(query, domainClass, collectionName);
|
||||
|
||||
return results.stream().map(d -> getConverter().read(domainClass, d)).collect(Collectors.toList());
|
||||
final List<CosmosItemProperties> documents = findDocuments(query, domainClass, collectionName);
|
||||
return documents.stream()
|
||||
.map(d -> getConverter().read(domainClass, d))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public void deleteAll(@NonNull String collectionName, @NonNull Class<?> domainClass) {
|
||||
|
@ -200,13 +201,7 @@ public class DocumentDbTemplate implements DocumentDbOperations, ApplicationCont
|
|||
@Override
|
||||
public void deleteCollection(@NonNull String collectionName) {
|
||||
Assert.hasText(collectionName, "collectionName should have text.");
|
||||
|
||||
try {
|
||||
getDocumentClient().deleteCollection(getCollectionLink(this.databaseName, collectionName), null);
|
||||
this.collectionCache.remove(collectionName);
|
||||
} catch (DocumentClientException ex) {
|
||||
throw new DocumentDBAccessException("failed to delete collection: " + collectionName, ex);
|
||||
}
|
||||
reactiveCosmosTemplate.deleteContainer(collectionName);
|
||||
}
|
||||
|
||||
public String getCollectionName(Class<?> domainClass) {
|
||||
|
@ -215,98 +210,15 @@ public class DocumentDbTemplate implements DocumentDbOperations, ApplicationCont
|
|||
return new DocumentDbEntityInformation<>(domainClass).getCollectionName();
|
||||
}
|
||||
|
||||
private Database createDatabaseIfNotExists(String dbName) {
|
||||
try {
|
||||
final List<Database> dbList = getDocumentClient()
|
||||
.queryDatabases(new SqlQuerySpec("SELECT * FROM root r WHERE r.id=@id",
|
||||
new SqlParameterCollection(new SqlParameter("@id", dbName))), null)
|
||||
.getQueryIterable().toList();
|
||||
|
||||
if (!dbList.isEmpty()) {
|
||||
return dbList.get(0);
|
||||
} else {
|
||||
// create new database
|
||||
final Database db = new Database();
|
||||
db.setId(dbName);
|
||||
|
||||
log.debug("execute createDatabase {}", dbName);
|
||||
|
||||
final Resource resource = getDocumentClient().createDatabase(db, null).getResource();
|
||||
|
||||
if (resource instanceof Database) {
|
||||
return (Database) resource;
|
||||
} else {
|
||||
final String errorMessage = MessageFormat.format(
|
||||
"create database {0} and get unexpected result: {1}", dbName, resource.getSelfLink());
|
||||
|
||||
log.error(errorMessage);
|
||||
throw new DatabaseCreationException(errorMessage);
|
||||
}
|
||||
}
|
||||
} catch (DocumentClientException ex) {
|
||||
throw new DocumentDBAccessException("createOrGetDatabase exception", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private DocumentCollection createCollection(@NonNull String dbName, String partitionKeyFieldName,
|
||||
@NonNull DocumentDbEntityInformation information) {
|
||||
DocumentCollection collection = new DocumentCollection();
|
||||
final String collectionName = information.getCollectionName();
|
||||
final IndexingPolicy policy = information.getIndexingPolicy();
|
||||
final Integer timeToLive = information.getTimeToLive();
|
||||
final RequestOptions requestOptions = getRequestOptions(null, information.getRequestUnit());
|
||||
|
||||
collection.setId(collectionName);
|
||||
collection.setIndexingPolicy(policy);
|
||||
|
||||
if (information.getIndexingPolicy().getAutomatic()) {
|
||||
collection.setDefaultTimeToLive(timeToLive); // If not Automatic, setDefaultTimeToLive is invalid
|
||||
}
|
||||
|
||||
if (partitionKeyFieldName != null && !partitionKeyFieldName.isEmpty()) {
|
||||
final PartitionKeyDefinition partitionKeyDefinition = new PartitionKeyDefinition();
|
||||
final ArrayList<String> paths = new ArrayList<>();
|
||||
|
||||
paths.add(getPartitionKeyPath(partitionKeyFieldName));
|
||||
partitionKeyDefinition.setPaths(paths);
|
||||
collection.setPartitionKey(partitionKeyDefinition);
|
||||
}
|
||||
|
||||
log.debug("execute createCollection in database {} collection {}", dbName, collectionName);
|
||||
|
||||
try {
|
||||
final Resource resource = getDocumentClient()
|
||||
.createCollection(getDatabaseLink(dbName), collection, requestOptions)
|
||||
.getResource();
|
||||
if (resource instanceof DocumentCollection) {
|
||||
collection = (DocumentCollection) resource;
|
||||
}
|
||||
return collection;
|
||||
} catch (DocumentClientException e) {
|
||||
throw new DocumentDBAccessException("createCollection exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocumentCollection createCollectionIfNotExists(@NonNull DocumentDbEntityInformation information) {
|
||||
if (this.databaseCache == null) {
|
||||
this.databaseCache = createDatabaseIfNotExists(this.databaseName);
|
||||
}
|
||||
|
||||
final String collectionName = information.getCollectionName();
|
||||
final String partitionKeyFieldName = information.getPartitionKeyFieldName();
|
||||
|
||||
final List<DocumentCollection> collectionList = getDocumentClient()
|
||||
.queryCollections(getDatabaseLink(this.databaseName),
|
||||
new SqlQuerySpec("SELECT * FROM root r WHERE r.id=@id",
|
||||
new SqlParameterCollection(new SqlParameter("@id", collectionName))), null)
|
||||
.getQueryIterable().toList();
|
||||
|
||||
if (!collectionList.isEmpty()) {
|
||||
return collectionList.get(0);
|
||||
} else {
|
||||
return createCollection(this.databaseName, partitionKeyFieldName, information);
|
||||
final CosmosContainerResponse response = reactiveCosmosTemplate
|
||||
.createCollectionIfNotExists(information)
|
||||
.block();
|
||||
if (response == null) {
|
||||
throw new DocumentDBAccessException("Failed to create collection");
|
||||
}
|
||||
return new DocumentCollection(response.properties().toJson());
|
||||
}
|
||||
|
||||
public void deleteById(String collectionName, Object id, PartitionKey partitionKey) {
|
||||
|
@ -315,74 +227,17 @@ public class DocumentDbTemplate implements DocumentDbOperations, ApplicationCont
|
|||
|
||||
log.debug("execute deleteById in database {} collection {}", this.databaseName, collectionName);
|
||||
|
||||
com.azure.data.cosmos.PartitionKey pk = toCosmosPartitionKey(partitionKey);
|
||||
if (pk == null) {
|
||||
pk = com.azure.data.cosmos.PartitionKey.None;
|
||||
}
|
||||
try {
|
||||
final RequestOptions options = getRequestOptions(partitionKey, null);
|
||||
getDocumentClient().deleteDocument(getDocumentLink(databaseName, collectionName, id.toString()), options);
|
||||
} catch (DocumentClientException ex) {
|
||||
throw new DocumentDBAccessException("deleteById exception", ex);
|
||||
reactiveCosmosTemplate.deleteById(collectionName, id, pk).block();
|
||||
} catch (Exception e) {
|
||||
throw new DocumentDBAccessException("deleteById exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getDatabaseLink(String databaseName) {
|
||||
return "dbs/" + databaseName;
|
||||
}
|
||||
|
||||
private String getCollectionLink(String databaseName, String collectionName) {
|
||||
return getDatabaseLink(databaseName) + "/colls/" + collectionName;
|
||||
}
|
||||
|
||||
private String getDocumentLink(String databaseName, String collectionName, Object documentId) {
|
||||
return getCollectionLink(databaseName, collectionName) + "/docs/" + documentId;
|
||||
}
|
||||
|
||||
private String getPartitionKeyPath(String partitionKey) {
|
||||
return "/" + partitionKey;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private DocumentDBConfig getDocumentDbConfig() {
|
||||
return documentDbFactory.getConfig();
|
||||
}
|
||||
|
||||
private RequestOptions getRequestOptions(PartitionKey key, Integer requestUnit) {
|
||||
final RequestOptions options = CosmosdbUtils.getCopyFrom(getDocumentDbConfig().getRequestOptions());
|
||||
|
||||
if (key != null) {
|
||||
options.setPartitionKey(key);
|
||||
}
|
||||
|
||||
if (requestUnit != null) {
|
||||
options.setOfferThroughput(requestUnit);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
private <T> List<T> executeQuery(@NonNull SqlQuerySpec sqlQuerySpec, boolean isCrossPartition,
|
||||
@NonNull Class<T> domainClass, String collectionName) {
|
||||
final FeedResponse<Document> feedResponse = executeQuery(sqlQuerySpec, isCrossPartition, collectionName);
|
||||
final List<Document> result = feedResponse.getQueryIterable().toList();
|
||||
|
||||
return result.stream().map(r -> getConverter().read(domainClass, r)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private FeedResponse<Document> executeQuery(@NonNull SqlQuerySpec sqlQuerySpec, boolean isCrossPartition,
|
||||
String collectionName) {
|
||||
final FeedOptions feedOptions = new FeedOptions();
|
||||
final String selfLink = getCollectionSelfLink(collectionName);
|
||||
|
||||
feedOptions.setEnableCrossPartitionQuery(isCrossPartition);
|
||||
|
||||
return getDocumentClient().queryDocuments(selfLink, sqlQuerySpec, feedOptions);
|
||||
}
|
||||
|
||||
private FeedResponse<Document> executeQuery(@NonNull SqlQuerySpec sqlQuerySpec, FeedOptions feedOptions,
|
||||
String collectionName) {
|
||||
final String selfLink = getCollectionSelfLink(collectionName);
|
||||
|
||||
return getDocumentClient().queryDocuments(selfLink, sqlQuerySpec, feedOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, ID> List<T> findByIds(Iterable<ID> ids, Class<T> entityClass, String collectionName) {
|
||||
Assert.notNull(ids, "Id list should not be null");
|
||||
|
@ -400,11 +255,8 @@ public class DocumentDbTemplate implements DocumentDbOperations, ApplicationCont
|
|||
Assert.hasText(collectionName, "collection should not be null, empty or only whitespaces");
|
||||
|
||||
try {
|
||||
final SqlQuerySpec sqlQuerySpec = new FindQuerySpecGenerator().generate(query);
|
||||
final boolean isCrossPartitionQuery = query.isCrossPartitionQuery(getPartitionKeyNames(domainClass));
|
||||
|
||||
return this.executeQuery(sqlQuerySpec, isCrossPartitionQuery, domainClass, collectionName);
|
||||
} catch (IllegalStateException | IllegalArgumentException e) {
|
||||
return reactiveCosmosTemplate.find(query, domainClass, collectionName).collectList().block();
|
||||
} catch (Exception e) {
|
||||
throw new DocumentDBAccessException("Failed to execute find operation from " + collectionName, e);
|
||||
}
|
||||
}
|
||||
|
@ -413,33 +265,6 @@ public class DocumentDbTemplate implements DocumentDbOperations, ApplicationCont
|
|||
return this.find(query, domainClass, collectionName).size() > 0;
|
||||
}
|
||||
|
||||
private List<Document> findDocuments(@NonNull DocumentQuery query, @NonNull Class<?> domainClass,
|
||||
@NonNull String collectionName) {
|
||||
final SqlQuerySpec sqlQuerySpec = new FindQuerySpecGenerator().generate(query);
|
||||
final boolean isCrossPartitionQuery = query.isCrossPartitionQuery(getPartitionKeyNames(domainClass));
|
||||
final FeedResponse<Document> response = executeQuery(sqlQuerySpec, isCrossPartitionQuery, collectionName);
|
||||
|
||||
return response.getQueryIterable().toList();
|
||||
}
|
||||
|
||||
private void deleteDocument(@NonNull Document document, @NonNull List<String> partitionKeyNames) {
|
||||
try {
|
||||
Assert.isTrue(partitionKeyNames.size() <= 1, "Only one Partition is supported.");
|
||||
|
||||
PartitionKey partitionKey = null;
|
||||
|
||||
if (!partitionKeyNames.isEmpty() && StringUtils.hasText(partitionKeyNames.get(0))) {
|
||||
partitionKey = new PartitionKey(document.get(partitionKeyNames.get(0)));
|
||||
}
|
||||
|
||||
final RequestOptions options = getRequestOptions(partitionKey, null);
|
||||
|
||||
getDocumentClient().deleteDocument(document.getSelfLink(), options);
|
||||
} catch (DocumentClientException e) {
|
||||
throw new DocumentDBAccessException("Failed to delete document: " + document.getSelfLink(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the DocumentQuery, need to query the domains at first, then delete the document
|
||||
* from the result.
|
||||
|
@ -458,12 +283,14 @@ public class DocumentDbTemplate implements DocumentDbOperations, ApplicationCont
|
|||
Assert.notNull(domainClass, "domainClass should not be null.");
|
||||
Assert.hasText(collectionName, "collection should not be null, empty or only whitespaces");
|
||||
|
||||
final List<Document> results = findDocuments(query, domainClass, collectionName);
|
||||
final List<CosmosItemProperties> results = findDocuments(query, domainClass, collectionName);
|
||||
final List<String> partitionKeyName = getPartitionKeyNames(domainClass);
|
||||
|
||||
results.forEach(d -> deleteDocument(d, partitionKeyName));
|
||||
results.forEach(d -> deleteDocument(d, partitionKeyName, collectionName));
|
||||
|
||||
return results.stream().map(d -> getConverter().read(domainClass, d)).collect(Collectors.toList());
|
||||
return results.stream()
|
||||
.map(d -> getConverter().read(domainClass, d))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -484,33 +311,42 @@ public class DocumentDbTemplate implements DocumentDbOperations, ApplicationCont
|
|||
final Pageable pageable = query.getPageable();
|
||||
final FeedOptions feedOptions = new FeedOptions();
|
||||
if (pageable instanceof DocumentDbPageRequest) {
|
||||
feedOptions.setRequestContinuation(((DocumentDbPageRequest) pageable).getRequestContinuation());
|
||||
feedOptions.requestContinuation(((DocumentDbPageRequest) pageable).getRequestContinuation());
|
||||
}
|
||||
|
||||
feedOptions.setPageSize(pageable.getPageSize());
|
||||
feedOptions.setEnableCrossPartitionQuery(query.isCrossPartitionQuery(getPartitionKeyNames(domainClass)));
|
||||
feedOptions.maxItemCount(pageable.getPageSize());
|
||||
feedOptions.enableCrossPartitionQuery(query.isCrossPartitionQuery(getPartitionKeyNames(domainClass)));
|
||||
|
||||
final SqlQuerySpec sqlQuerySpec = new FindQuerySpecGenerator().generate(query);
|
||||
final FeedResponse<Document> response = executeQuery(sqlQuerySpec, feedOptions, collectionName);
|
||||
final SqlQuerySpec sqlQuerySpec = new FindQuerySpecGenerator().generateCosmos(query);
|
||||
final FeedResponse<CosmosItemProperties> feedResponse =
|
||||
cosmosClient.getDatabase(this.databaseName)
|
||||
.getContainer(collectionName)
|
||||
.queryItems(sqlQuerySpec, feedOptions)
|
||||
.next()
|
||||
.block();
|
||||
|
||||
final Iterator<Document> it = response.getQueryIterator();
|
||||
if (feedResponse == null) {
|
||||
throw new DocumentDBAccessException("Failed to query documents");
|
||||
}
|
||||
|
||||
final Iterator<CosmosItemProperties> it = feedResponse.results().iterator();
|
||||
|
||||
final List<T> result = new ArrayList<>();
|
||||
for (int index = 0; it.hasNext() && index < pageable.getPageSize(); index++) {
|
||||
// Limit iterator as inner iterator will automatically fetch the next page
|
||||
final Document doc = it.next();
|
||||
if (doc == null) {
|
||||
|
||||
final CosmosItemProperties cosmosItemProperties = it.next();
|
||||
if (cosmosItemProperties == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final T entity = mappingDocumentDbConverter.read(domainClass, doc);
|
||||
final T entity = mappingDocumentDbConverter.read(domainClass, cosmosItemProperties);
|
||||
result.add(entity);
|
||||
}
|
||||
|
||||
final DocumentDbPageRequest pageRequest = DocumentDbPageRequest.of(pageable.getPageNumber(),
|
||||
pageable.getPageSize(),
|
||||
response.getResponseContinuation(),
|
||||
query.getSort());
|
||||
pageable.getPageSize(),
|
||||
feedResponse.continuationToken(),
|
||||
query.getSort());
|
||||
|
||||
return new PageImpl<>(result, pageRequest, count(query, domainClass, collectionName));
|
||||
}
|
||||
|
@ -519,10 +355,11 @@ public class DocumentDbTemplate implements DocumentDbOperations, ApplicationCont
|
|||
public long count(String collectionName) {
|
||||
Assert.hasText(collectionName, "collectionName should not be empty");
|
||||
|
||||
final DocumentQuery query = new DocumentQuery(Criteria.getInstance(CriteriaType.ALL));
|
||||
final SqlQuerySpec querySpec = new CountQueryGenerator().generate(query);
|
||||
|
||||
return getCountValue(querySpec, true, collectionName);
|
||||
final Long count = reactiveCosmosTemplate.count(collectionName).block();
|
||||
if (count == null) {
|
||||
throw new DocumentDBAccessException("Failed to get count for collectionName: " + collectionName);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -530,27 +367,12 @@ public class DocumentDbTemplate implements DocumentDbOperations, ApplicationCont
|
|||
Assert.notNull(domainClass, "domainClass should not be null");
|
||||
Assert.hasText(collectionName, "collectionName should not be empty");
|
||||
|
||||
final SqlQuerySpec querySpec = new CountQueryGenerator().generate(query);
|
||||
final boolean isCrossPartitionQuery = query.isCrossPartitionQuery(getPartitionKeyNames(domainClass));
|
||||
|
||||
return getCountValue(querySpec, isCrossPartitionQuery, collectionName);
|
||||
}
|
||||
|
||||
private long getCountValue(SqlQuerySpec querySpec, boolean isCrossPartitionQuery, String collectionName) {
|
||||
final FeedResponse<Document> feedResponse = executeQuery(querySpec, isCrossPartitionQuery, collectionName);
|
||||
final Object value = feedResponse.getQueryIterable().toList().get(0).getHashMap().get(COUNT_VALUE_KEY);
|
||||
|
||||
if (value instanceof Integer) {
|
||||
return Long.valueOf((Integer) value);
|
||||
} else if (value instanceof Long) {
|
||||
return (Long) value;
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected value type " + value.getClass() + " of value: " + value);
|
||||
final Long count = reactiveCosmosTemplate.count(query, isCrossPartitionQuery, collectionName).block();
|
||||
if (count == null) {
|
||||
throw new DocumentDBAccessException("Failed to get count for collectionName: " + collectionName);
|
||||
}
|
||||
}
|
||||
|
||||
private String getCollectionSelfLink(@NonNull String collectionName) {
|
||||
return String.format("dbs/%s/colls/%s", this.databaseName, collectionName);
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -569,10 +391,59 @@ public class DocumentDbTemplate implements DocumentDbOperations, ApplicationCont
|
|||
return Collections.singletonList(entityInfo.getPartitionKeyFieldName());
|
||||
}
|
||||
|
||||
private com.azure.data.cosmos.PartitionKey toCosmosPartitionKey(PartitionKey partitionKey) {
|
||||
if (partitionKey == null) {
|
||||
return null;
|
||||
}
|
||||
return com.azure.data.cosmos.PartitionKey.fromJsonString(partitionKey.getInternalPartitionKey().toJson());
|
||||
}
|
||||
|
||||
private void assertValidId(Object id) {
|
||||
Assert.notNull(id, "id should not be null");
|
||||
if (id instanceof String) {
|
||||
Assert.hasText(id.toString(), "id should not be empty or only whitespaces.");
|
||||
}
|
||||
}
|
||||
|
||||
private List<CosmosItemProperties> findDocuments(@NonNull DocumentQuery query, @NonNull Class<?> domainClass,
|
||||
@NonNull String containerName) {
|
||||
final SqlQuerySpec sqlQuerySpec = new FindQuerySpecGenerator().generateCosmos(query);
|
||||
final boolean isCrossPartitionQuery = query.isCrossPartitionQuery(getPartitionKeyNames(domainClass));
|
||||
final FeedOptions feedOptions = new FeedOptions();
|
||||
feedOptions.enableCrossPartitionQuery(isCrossPartitionQuery);
|
||||
return cosmosClient
|
||||
.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.queryItems(sqlQuerySpec, feedOptions)
|
||||
.flatMap(cosmosItemFeedResponse -> Flux.fromIterable(cosmosItemFeedResponse.results()))
|
||||
.collectList()
|
||||
.block();
|
||||
}
|
||||
|
||||
private CosmosItemResponse deleteDocument(@NonNull CosmosItemProperties cosmosItemProperties,
|
||||
@NonNull List<String> partitionKeyNames,
|
||||
String containerName) {
|
||||
Assert.isTrue(partitionKeyNames.size() <= 1, "Only one Partition is supported.");
|
||||
|
||||
PartitionKey partitionKey = null;
|
||||
|
||||
if (!partitionKeyNames.isEmpty() && StringUtils.hasText(partitionKeyNames.get(0))) {
|
||||
partitionKey = new PartitionKey(cosmosItemProperties.get(partitionKeyNames.get(0)));
|
||||
}
|
||||
|
||||
com.azure.data.cosmos.PartitionKey pk = toCosmosPartitionKey(partitionKey);
|
||||
|
||||
if (pk == null) {
|
||||
pk = com.azure.data.cosmos.PartitionKey.None;
|
||||
}
|
||||
|
||||
final CosmosItemRequestOptions options = new CosmosItemRequestOptions(pk);
|
||||
|
||||
return cosmosClient
|
||||
.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.getItem(cosmosItemProperties.id(), partitionKey)
|
||||
.delete(options)
|
||||
.block();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,10 +23,7 @@ import com.microsoft.azure.spring.data.cosmosdb.core.query.CriteriaType;
|
|||
import com.microsoft.azure.spring.data.cosmosdb.core.query.DocumentQuery;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.exception.DocumentDBAccessException;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.repository.support.DocumentDbEntityInformation;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.json.JSONObject;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
|
@ -39,16 +36,13 @@ import reactor.core.publisher.Mono;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
public class ReactiveCosmosTemplate implements ReactiveCosmosOperations, ApplicationContextAware {
|
||||
private static final String COUNT_VALUE_KEY = "_aggregate";
|
||||
public static final int DEFAULT_THROUGHPUT = 400;
|
||||
|
||||
private final String databaseName;
|
||||
|
||||
@Getter(AccessLevel.PRIVATE)
|
||||
private final CosmosClient cosmosClient;
|
||||
|
||||
private final List<String> collectionCache;
|
||||
|
@ -89,14 +83,12 @@ public class ReactiveCosmosTemplate implements ReactiveCosmosOperations, Applica
|
|||
@Override
|
||||
public Mono<CosmosContainerResponse> createCollectionIfNotExists(DocumentDbEntityInformation information) {
|
||||
|
||||
// Note: Once Cosmos DB publishes new build, we shouldn't pass DEFAULT_THROUGHPUT.
|
||||
// There is a NPE if we call the other overloaded method which automatically passes options as null.
|
||||
return cosmosClient
|
||||
.createDatabaseIfNotExists(this.databaseName)
|
||||
.flatMap(cosmosDatabaseResponse -> cosmosDatabaseResponse
|
||||
.database()
|
||||
.createContainerIfNotExists(information.getCollectionName(),
|
||||
"/" + information.getPartitionKeyFieldName(), DEFAULT_THROUGHPUT)
|
||||
"/" + information.getPartitionKeyFieldName())
|
||||
.map(cosmosContainerResponse -> {
|
||||
this.collectionCache.add(information.getCollectionName());
|
||||
return cosmosContainerResponse;
|
||||
|
@ -159,14 +151,15 @@ public class ReactiveCosmosTemplate implements ReactiveCosmosOperations, Applica
|
|||
final String query = String.format("select * from root where root.id = '%s'", id.toString());
|
||||
final FeedOptions options = new FeedOptions();
|
||||
options.enableCrossPartitionQuery(true);
|
||||
return getCosmosClient().getDatabase(databaseName)
|
||||
return cosmosClient.getDatabase(databaseName)
|
||||
.getContainer(containerName)
|
||||
.queryItems(query, options)
|
||||
.flatMap(cosmosItemFeedResponse -> Mono.just(cosmosItemFeedResponse
|
||||
.flatMap(cosmosItemFeedResponse -> Mono.justOrEmpty(cosmosItemFeedResponse
|
||||
.results()
|
||||
.stream()
|
||||
.map(cosmosItem -> cosmosItem.toObject(entityClass))
|
||||
.collect(Collectors.toList()).get(0)))
|
||||
.findFirst()))
|
||||
.onErrorResume(Mono::error)
|
||||
.next();
|
||||
}
|
||||
|
||||
|
@ -193,10 +186,10 @@ public class ReactiveCosmosTemplate implements ReactiveCosmosOperations, Applica
|
|||
Assert.notNull(objectToSave, "objectToSave should not be null");
|
||||
|
||||
final Class<T> domainClass = (Class<T>) objectToSave.getClass();
|
||||
return getCosmosClient().getDatabase(this.databaseName)
|
||||
return cosmosClient.getDatabase(this.databaseName)
|
||||
.getContainer(getContainerName(objectToSave.getClass()))
|
||||
.createItem(objectToSave, new CosmosItemRequestOptions())
|
||||
.doOnError(Mono::error)
|
||||
.onErrorResume(Mono::error)
|
||||
.flatMap(cosmosItemResponse -> Mono.just(cosmosItemResponse.properties().toObject(domainClass)));
|
||||
}
|
||||
|
||||
|
@ -218,7 +211,7 @@ public class ReactiveCosmosTemplate implements ReactiveCosmosOperations, Applica
|
|||
}
|
||||
|
||||
final Class<T> domainClass = (Class<T>) objectToSave.getClass();
|
||||
return getCosmosClient().getDatabase(this.databaseName)
|
||||
return cosmosClient.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.createItem(objectToSave, options)
|
||||
.onErrorResume(Mono::error)
|
||||
|
@ -253,10 +246,11 @@ public class ReactiveCosmosTemplate implements ReactiveCosmosOperations, Applica
|
|||
options.partitionKey(partitionKey);
|
||||
}
|
||||
|
||||
return getCosmosClient().getDatabase(this.databaseName)
|
||||
return cosmosClient.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.upsertItem(object, options)
|
||||
.flatMap(cosmosItemResponse -> Mono.just(cosmosItemResponse.properties().toObject(domainClass)));
|
||||
.flatMap(cosmosItemResponse -> Mono.just(cosmosItemResponse.properties().toObject(domainClass)))
|
||||
.onErrorResume(Mono::error);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -273,15 +267,13 @@ public class ReactiveCosmosTemplate implements ReactiveCosmosOperations, Applica
|
|||
assertValidId(id);
|
||||
Assert.notNull(partitionKey, "partitionKey should not be null");
|
||||
|
||||
final JSONObject jo = new JSONObject();
|
||||
jo.put("id", id.toString());
|
||||
final CosmosItemRequestOptions options = new CosmosItemRequestOptions();
|
||||
options.partitionKey(partitionKey);
|
||||
return getCosmosClient().getDatabase(this.databaseName)
|
||||
return cosmosClient.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.getItem(id.toString(), partitionKey)
|
||||
.delete(options)
|
||||
.onErrorResume(throwable -> Mono.empty())
|
||||
.onErrorResume(Mono::error)
|
||||
.then();
|
||||
}
|
||||
|
||||
|
@ -303,13 +295,13 @@ public class ReactiveCosmosTemplate implements ReactiveCosmosOperations, Applica
|
|||
final FeedOptions options = new FeedOptions();
|
||||
final boolean isCrossPartitionQuery = query.isCrossPartitionQuery(Collections.singletonList(partitionKeyName));
|
||||
options.enableCrossPartitionQuery(isCrossPartitionQuery);
|
||||
return getCosmosClient().getDatabase(this.databaseName)
|
||||
return cosmosClient.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.queryItems(sqlQuerySpec, options)
|
||||
.onErrorResume(this::databaseAccessExceptionHandler)
|
||||
.map(cosmosItemFeedResponse -> cosmosItemFeedResponse.results()
|
||||
.stream()
|
||||
.map(cosmosItemProperties -> getCosmosClient()
|
||||
.map(cosmosItemProperties -> cosmosClient
|
||||
.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.getItem(cosmosItemProperties.id(), partitionKeyName)
|
||||
|
@ -363,7 +355,7 @@ public class ReactiveCosmosTemplate implements ReactiveCosmosOperations, Applica
|
|||
*/
|
||||
@Override
|
||||
public Mono<Boolean> exists(DocumentQuery query, Class<?> entityClass, String containerName) {
|
||||
return getCountValue(query, true, containerName).flatMap(count -> Mono.just(count > 0));
|
||||
return count(query, true, containerName).flatMap(count -> Mono.just(count > 0));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -387,7 +379,7 @@ public class ReactiveCosmosTemplate implements ReactiveCosmosOperations, Applica
|
|||
@Override
|
||||
public Mono<Long> count(String containerName) {
|
||||
final DocumentQuery query = new DocumentQuery(Criteria.getInstance(CriteriaType.ALL));
|
||||
return getCountValue(query, true, containerName);
|
||||
return count(query, true, containerName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -399,7 +391,11 @@ public class ReactiveCosmosTemplate implements ReactiveCosmosOperations, Applica
|
|||
*/
|
||||
@Override
|
||||
public Mono<Long> count(DocumentQuery query, String containerName) {
|
||||
return getCountValue(query, true, containerName);
|
||||
return count(query, true, containerName);
|
||||
}
|
||||
|
||||
public Mono<Long> count(DocumentQuery query, boolean isCrossPartitionQuery, String containerName) {
|
||||
return getCountValue(query, isCrossPartitionQuery, containerName);
|
||||
}
|
||||
|
||||
private Mono<Long> getCountValue(DocumentQuery query, boolean isCrossPartitionQuery, String containerName) {
|
||||
|
@ -417,7 +413,7 @@ public class ReactiveCosmosTemplate implements ReactiveCosmosOperations, Applica
|
|||
private Flux<FeedResponse<CosmosItemProperties>> executeQuery(SqlQuerySpec sqlQuerySpec, String collectionName,
|
||||
FeedOptions options) {
|
||||
|
||||
return getCosmosClient().getDatabase(this.databaseName)
|
||||
return cosmosClient.getDatabase(this.databaseName)
|
||||
.getContainer(collectionName)
|
||||
.queryItems(sqlQuerySpec, options);
|
||||
}
|
||||
|
@ -435,7 +431,7 @@ public class ReactiveCosmosTemplate implements ReactiveCosmosOperations, Applica
|
|||
public void deleteContainer(@NonNull String containerName) {
|
||||
Assert.hasText(containerName, "containerName should have text.");
|
||||
try {
|
||||
getCosmosClient().getDatabase(this.databaseName).getContainer(containerName).delete().block();
|
||||
cosmosClient.getDatabase(this.databaseName).getContainer(containerName).delete().block();
|
||||
this.collectionCache.remove(containerName);
|
||||
} catch (Exception e) {
|
||||
throw new DocumentDBAccessException("failed to delete collection: " + containerName, e);
|
||||
|
@ -458,7 +454,7 @@ public class ReactiveCosmosTemplate implements ReactiveCosmosOperations, Applica
|
|||
final boolean isCrossPartitionQuery = query.isCrossPartitionQuery(getPartitionKeyNames(domainClass));
|
||||
final FeedOptions feedOptions = new FeedOptions();
|
||||
feedOptions.enableCrossPartitionQuery(isCrossPartitionQuery);
|
||||
return getCosmosClient()
|
||||
return cosmosClient
|
||||
.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.queryItems(sqlQuerySpec, feedOptions)
|
||||
|
@ -482,9 +478,9 @@ public class ReactiveCosmosTemplate implements ReactiveCosmosOperations, Applica
|
|||
return Collections.singletonList(entityInfo.getPartitionKeyFieldName());
|
||||
}
|
||||
|
||||
private Mono<Void> deleteDocument(@NonNull CosmosItemProperties cosmosItemProperties,
|
||||
@NonNull List<String> partitionKeyNames,
|
||||
String containerName) {
|
||||
private Mono<CosmosItemProperties> deleteDocument(@NonNull CosmosItemProperties cosmosItemProperties,
|
||||
@NonNull List<String> partitionKeyNames,
|
||||
String containerName) {
|
||||
Assert.isTrue(partitionKeyNames.size() <= 1, "Only one Partition is supported.");
|
||||
|
||||
PartitionKey partitionKey = null;
|
||||
|
@ -495,12 +491,12 @@ public class ReactiveCosmosTemplate implements ReactiveCosmosOperations, Applica
|
|||
|
||||
final CosmosItemRequestOptions options = new CosmosItemRequestOptions(partitionKey);
|
||||
|
||||
return getCosmosClient()
|
||||
return cosmosClient
|
||||
.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.getItem(cosmosItemProperties.id(), partitionKey)
|
||||
.delete(options)
|
||||
.then();
|
||||
.map(cosmosItemResponse -> cosmosItemProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core.convert;
|
||||
|
||||
import com.azure.data.cosmos.CosmosItemProperties;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
@ -35,44 +36,46 @@ import java.util.Date;
|
|||
import static com.microsoft.azure.spring.data.cosmosdb.Constants.ISO_8601_COMPATIBLE_DATE_PATTERN;
|
||||
|
||||
public class MappingDocumentDbConverter
|
||||
implements EntityConverter<DocumentDbPersistentEntity<?>, DocumentDbPersistentProperty, Object, Document>,
|
||||
ApplicationContextAware {
|
||||
implements EntityConverter<DocumentDbPersistentEntity<?>, DocumentDbPersistentProperty,
|
||||
Object, CosmosItemProperties>,
|
||||
ApplicationContextAware {
|
||||
|
||||
protected final MappingContext<? extends DocumentDbPersistentEntity<?>,
|
||||
DocumentDbPersistentProperty> mappingContext;
|
||||
DocumentDbPersistentProperty> mappingContext;
|
||||
protected GenericConversionService conversionService;
|
||||
private ApplicationContext applicationContext;
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
public MappingDocumentDbConverter(
|
||||
MappingContext<? extends DocumentDbPersistentEntity<?>, DocumentDbPersistentProperty> mappingContext,
|
||||
@Qualifier(Constants.OBJECTMAPPER_BEAN_NAME) ObjectMapper objectMapper) {
|
||||
MappingContext<? extends DocumentDbPersistentEntity<?>, DocumentDbPersistentProperty> mappingContext,
|
||||
@Qualifier(Constants.OBJECTMAPPER_BEAN_NAME) ObjectMapper objectMapper) {
|
||||
this.mappingContext = mappingContext;
|
||||
this.conversionService = new GenericConversionService();
|
||||
this.objectMapper = objectMapper == null ? ObjectMapperFactory.getObjectMapper() : objectMapper;
|
||||
this.objectMapper = objectMapper == null ? ObjectMapperFactory.getObjectMapper() :
|
||||
objectMapper;
|
||||
this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
this.objectMapper.registerModule(provideAdvancedSerializersModule());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R extends Object> R read(Class<R> type, Document sourceDocument) {
|
||||
if (sourceDocument == null) {
|
||||
public <R> R read(Class<R> type, CosmosItemProperties cosmosItemProperties) {
|
||||
if (cosmosItemProperties == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final DocumentDbPersistentEntity<?> entity = mappingContext.getPersistentEntity(type);
|
||||
Assert.notNull(entity, "Entity is null.");
|
||||
|
||||
return readInternal(entity, type, sourceDocument);
|
||||
return readInternal(entity, type, cosmosItemProperties);
|
||||
}
|
||||
|
||||
protected <R extends Object> R readInternal(final DocumentDbPersistentEntity<?> entity, Class<R> type,
|
||||
final Document sourceDocument) {
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
objectMapper.registerModule(provideAdvancedSerializersModule());
|
||||
private <R> R readInternal(final DocumentDbPersistentEntity<?> entity, Class<R> type,
|
||||
final CosmosItemProperties cosmosItemProperties) {
|
||||
|
||||
try {
|
||||
final DocumentDbPersistentProperty idProperty = entity.getIdProperty();
|
||||
final Object idValue = sourceDocument.getId();
|
||||
final JSONObject jsonObject = new JSONObject(sourceDocument.toJson());
|
||||
final Object idValue = cosmosItemProperties.id();
|
||||
final JSONObject jsonObject = new JSONObject(cosmosItemProperties.toJson());
|
||||
|
||||
if (idProperty != null) {
|
||||
// Replace the key id to the actual id field name in domain
|
||||
|
@ -82,8 +85,8 @@ public class MappingDocumentDbConverter
|
|||
|
||||
return objectMapper.readValue(jsonObject.toString(), type);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to read the source document " + sourceDocument.toJson()
|
||||
+ " to target type " + type, e);
|
||||
throw new IllegalStateException("Failed to read the source document " + cosmosItemProperties.toJson()
|
||||
+ " to target type " + type, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,7 +98,7 @@ public class MappingDocumentDbConverter
|
|||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void write(Object sourceEntity, Document document) {
|
||||
public void write(Object sourceEntity, CosmosItemProperties document) {
|
||||
throw new UnsupportedOperationException("The feature is not implemented yet");
|
||||
}
|
||||
|
||||
|
@ -105,7 +108,7 @@ public class MappingDocumentDbConverter
|
|||
}
|
||||
|
||||
final DocumentDbPersistentEntity<?> persistentEntity =
|
||||
mappingContext.getPersistentEntity(sourceEntity.getClass());
|
||||
mappingContext.getPersistentEntity(sourceEntity.getClass());
|
||||
|
||||
if (persistentEntity == null) {
|
||||
throw new MappingException("no mapping metadata for entity type: " + sourceEntity.getClass().getName());
|
||||
|
@ -130,6 +133,38 @@ public class MappingDocumentDbConverter
|
|||
return document;
|
||||
}
|
||||
|
||||
public CosmosItemProperties writeCosmosItemProperties(Object sourceEntity) {
|
||||
if (sourceEntity == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final DocumentDbPersistentEntity<?> persistentEntity =
|
||||
mappingContext.getPersistentEntity(sourceEntity.getClass());
|
||||
|
||||
if (persistentEntity == null) {
|
||||
throw new MappingException("no mapping metadata for entity type: " + sourceEntity.getClass().getName());
|
||||
}
|
||||
|
||||
final ConvertingPropertyAccessor accessor = getPropertyAccessor(sourceEntity);
|
||||
final DocumentDbPersistentProperty idProperty = persistentEntity.getIdProperty();
|
||||
final CosmosItemProperties cosmosItemProperties;
|
||||
|
||||
try {
|
||||
cosmosItemProperties =
|
||||
new CosmosItemProperties(objectMapper.writeValueAsString(sourceEntity));
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new DocumentDBAccessException("Failed to map document value.", e);
|
||||
}
|
||||
|
||||
if (idProperty != null) {
|
||||
final Object value = accessor.getProperty(idProperty);
|
||||
final String id = value == null ? null : value.toString();
|
||||
cosmosItemProperties.id(id);
|
||||
}
|
||||
|
||||
return cosmosItemProperties;
|
||||
}
|
||||
|
||||
public ApplicationContext getApplicationContext() {
|
||||
return this.applicationContext;
|
||||
}
|
||||
|
@ -150,7 +185,8 @@ public class MappingDocumentDbConverter
|
|||
|
||||
|
||||
private ConvertingPropertyAccessor getPropertyAccessor(Object entity) {
|
||||
final DocumentDbPersistentEntity<?> entityInformation = mappingContext.getPersistentEntity(entity.getClass());
|
||||
final DocumentDbPersistentEntity<?> entityInformation =
|
||||
mappingContext.getPersistentEntity(entity.getClass());
|
||||
|
||||
Assert.notNull(entityInformation, "EntityInformation should not be null.");
|
||||
final PersistentPropertyAccessor accessor = entityInformation.getPropertyAccessor(entity);
|
||||
|
|
|
@ -19,12 +19,14 @@ import org.junit.Test;
|
|||
import org.junit.rules.ExpectedException;
|
||||
import org.mockito.Mock;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
|
||||
public class AbstractDocumentDbConfigurationUnitTest {
|
||||
public class AbstractDocumentDbConfigurationIT {
|
||||
private static final String OBJECTMAPPER_BEAN_NAME = Constants.OBJECTMAPPER_BEAN_NAME;
|
||||
|
||||
@Rule
|
||||
|
@ -72,13 +74,21 @@ public class AbstractDocumentDbConfigurationUnitTest {
|
|||
}
|
||||
|
||||
@Configuration
|
||||
@PropertySource(value = {"classpath:application.properties"})
|
||||
static class TestDocumentDbConfiguration extends AbstractDocumentDbConfiguration {
|
||||
|
||||
@Value("${cosmosdb.uri:}")
|
||||
private String documentDbUri;
|
||||
|
||||
@Value("${cosmosdb.key:}")
|
||||
private String documentDbKey;
|
||||
|
||||
@Mock
|
||||
private DocumentClient mockClient;
|
||||
|
||||
@Bean
|
||||
public DocumentDBConfig getConfig() {
|
||||
return DocumentDBConfig.builder("http://fake-uri", "fake-key", TestConstants.DB_NAME).build();
|
||||
return DocumentDBConfig.builder(documentDbUri, documentDbKey, TestConstants.DB_NAME).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -96,8 +106,15 @@ public class AbstractDocumentDbConfigurationUnitTest {
|
|||
}
|
||||
|
||||
@Configuration
|
||||
@PropertySource(value = {"classpath:application.properties"})
|
||||
static class RequestOptionsConfiguration extends AbstractDocumentDbConfiguration {
|
||||
|
||||
@Value("${cosmosdb.uri:}")
|
||||
private String documentDbUri;
|
||||
|
||||
@Value("${cosmosdb.key:}")
|
||||
private String documentDbKey;
|
||||
|
||||
private RequestOptions getRequestOptions() {
|
||||
final RequestOptions options = new RequestOptions();
|
||||
|
||||
|
@ -111,7 +128,7 @@ public class AbstractDocumentDbConfigurationUnitTest {
|
|||
@Bean
|
||||
public DocumentDBConfig getConfig() {
|
||||
final RequestOptions options = getRequestOptions();
|
||||
return DocumentDBConfig.builder("http://fake-uri", "fake-key", TestConstants.DB_NAME)
|
||||
return DocumentDBConfig.builder(documentDbUri, documentDbKey, TestConstants.DB_NAME)
|
||||
.requestOptions(options)
|
||||
.build();
|
||||
}
|
|
@ -8,7 +8,7 @@ package com.microsoft.azure.spring.data.cosmosdb.core;
|
|||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.microsoft.azure.documentdb.DocumentCollection;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.DocumentDbFactory;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.CosmosDbFactory;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.common.TestConstants;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.config.DocumentDBConfig;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.convert.MappingDocumentDbConverter;
|
||||
|
@ -36,7 +36,7 @@ import org.springframework.data.domain.PageRequest;
|
|||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -44,7 +44,7 @@ import static com.microsoft.azure.spring.data.cosmosdb.common.PageTestUtils.vali
|
|||
import static com.microsoft.azure.spring.data.cosmosdb.common.PageTestUtils.validateNonLastPage;
|
||||
import static com.microsoft.azure.spring.data.cosmosdb.common.TestConstants.*;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@PropertySource(value = {"classpath:application.properties"})
|
||||
|
@ -77,7 +77,7 @@ public class DocumentDbTemplateIT {
|
|||
@Before
|
||||
public void setup() throws ClassNotFoundException {
|
||||
final DocumentDBConfig dbConfig = DocumentDBConfig.builder(documentDbUri, documentDbKey, DB_NAME).build();
|
||||
final DocumentDbFactory dbFactory = new DocumentDbFactory(dbConfig);
|
||||
final CosmosDbFactory cosmosDbFactory = new CosmosDbFactory(dbConfig);
|
||||
|
||||
mappingContext = new DocumentDbMappingContext();
|
||||
objectMapper = new ObjectMapper();
|
||||
|
@ -87,7 +87,7 @@ public class DocumentDbTemplateIT {
|
|||
mappingContext.setInitialEntitySet(new EntityScanner(this.applicationContext).scan(Persistent.class));
|
||||
|
||||
dbConverter = new MappingDocumentDbConverter(mappingContext, objectMapper);
|
||||
dbTemplate = new DocumentDbTemplate(dbFactory, dbConverter, DB_NAME);
|
||||
dbTemplate = new DocumentDbTemplate(cosmosDbFactory, dbConverter, DB_NAME);
|
||||
|
||||
collectionPerson = dbTemplate.createCollectionIfNotExists(this.personInfo);
|
||||
dbTemplate.insert(Person.class.getSimpleName(), TEST_PERSON, null);
|
||||
|
@ -114,7 +114,7 @@ public class DocumentDbTemplateIT {
|
|||
public void testFindById() {
|
||||
final Person result = dbTemplate.findById(Person.class.getSimpleName(),
|
||||
TEST_PERSON.getId(), Person.class);
|
||||
assertTrue(result.equals(TEST_PERSON));
|
||||
assertEquals(result, TEST_PERSON);
|
||||
|
||||
final Person nullResult = dbTemplate.findById(Person.class.getSimpleName(),
|
||||
TestConstants.NOT_EXIST_ID, Person.class);
|
||||
|
@ -140,14 +140,14 @@ public class DocumentDbTemplateIT {
|
|||
dbTemplate.deleteById(Person.class.getSimpleName(), TEST_PERSON.getId(), null);
|
||||
|
||||
final String firstName = TestConstants.NEW_FIRST_NAME + "_" + UUID.randomUUID().toString();
|
||||
final Person newPerson = new Person(null, firstName, TestConstants.NEW_FIRST_NAME, null, null);
|
||||
final Person newPerson = new Person(TEST_PERSON.getId(), firstName, TestConstants.NEW_FIRST_NAME, null, null);
|
||||
|
||||
dbTemplate.upsert(Person.class.getSimpleName(), newPerson, null);
|
||||
|
||||
final List<Person> result = dbTemplate.findAll(Person.class);
|
||||
|
||||
assertThat(result.size()).isEqualTo(1);
|
||||
assertTrue(result.get(0).getFirstName().equals(firstName));
|
||||
assertEquals(result.get(0).getFirstName(), firstName);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -159,7 +159,7 @@ public class DocumentDbTemplateIT {
|
|||
final Person result = dbTemplate.findById(Person.class.getSimpleName(),
|
||||
updated.getId(), Person.class);
|
||||
|
||||
assertTrue(result.equals(updated));
|
||||
assertEquals(result, updated);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -171,7 +171,7 @@ public class DocumentDbTemplateIT {
|
|||
|
||||
final List<Person> result = dbTemplate.findAll(Person.class);
|
||||
assertThat(result.size()).isEqualTo(1);
|
||||
assertTrue(result.get(0).equals(TEST_PERSON_2));
|
||||
assertEquals(result.get(0), TEST_PERSON_2);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -190,7 +190,7 @@ public class DocumentDbTemplateIT {
|
|||
dbTemplate.insert(TEST_PERSON_2, null);
|
||||
|
||||
final Criteria criteria = Criteria.getInstance(CriteriaType.IS_EQUAL, "firstName",
|
||||
Arrays.asList(TEST_PERSON_2.getFirstName()));
|
||||
Collections.singletonList(TEST_PERSON_2.getFirstName()));
|
||||
final DocumentQuery query = new DocumentQuery(criteria);
|
||||
|
||||
final long count = dbTemplate.count(query, Person.class, collectionName);
|
||||
|
@ -217,7 +217,7 @@ public class DocumentDbTemplateIT {
|
|||
dbTemplate.insert(TEST_PERSON_2, null);
|
||||
|
||||
final Criteria criteria = Criteria.getInstance(CriteriaType.IS_EQUAL, "firstName",
|
||||
Arrays.asList(TestConstants.FIRST_NAME));
|
||||
Collections.singletonList(FIRST_NAME));
|
||||
final PageRequest pageRequest = new DocumentDbPageRequest(0, PAGE_SIZE_2, null);
|
||||
final DocumentQuery query = new DocumentQuery(criteria).with(pageRequest);
|
||||
|
||||
|
|
|
@ -6,10 +6,6 @@
|
|||
package com.microsoft.azure.spring.data.cosmosdb.core;
|
||||
|
||||
import com.microsoft.azure.documentdb.PartitionKey;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.DocumentDbFactory;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.common.TestConstants;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.config.DocumentDBConfig;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.convert.MappingDocumentDbConverter;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.query.Criteria;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.query.DocumentQuery;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.domain.Person;
|
||||
|
@ -17,6 +13,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.util.Assert;
|
||||
|
@ -37,16 +34,12 @@ public class DocumentDbTemplateIllegalTest {
|
|||
private static final String WHITESPACES_STR = " ";
|
||||
private static final String CHECK_FAILURE_MSG = "Illegal argument is not checked";
|
||||
|
||||
@Mock(answer = Answers.CALLS_REAL_METHODS)
|
||||
private DocumentDbTemplate dbTemplate;
|
||||
private Class dbTemplateClass;
|
||||
|
||||
@Mock
|
||||
MappingDocumentDbConverter dbConverter;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
final DocumentDBConfig dbConfig = DocumentDBConfig.builder("http://uri", "key", TestConstants.DB_NAME).build();
|
||||
this.dbTemplate = new DocumentDbTemplate(new DocumentDbFactory(dbConfig), dbConverter, TestConstants.DB_NAME);
|
||||
dbTemplateClass = dbTemplate.getClass();
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ package com.microsoft.azure.spring.data.cosmosdb.core;
|
|||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.microsoft.azure.documentdb.PartitionKey;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.DocumentDbFactory;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.CosmosDbFactory;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.config.DocumentDBConfig;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.convert.MappingDocumentDbConverter;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.mapping.DocumentDbMappingContext;
|
||||
|
@ -68,7 +68,7 @@ public class DocumentDbTemplatePartitionIT {
|
|||
@Before
|
||||
public void setup() throws ClassNotFoundException {
|
||||
final DocumentDBConfig dbConfig = DocumentDBConfig.builder(documentDbUri, documentDbKey, DB_NAME).build();
|
||||
final DocumentDbFactory dbFactory = new DocumentDbFactory(dbConfig);
|
||||
final CosmosDbFactory cosmosDbFactory = new CosmosDbFactory(dbConfig);
|
||||
final ObjectMapper objectMapper = new ObjectMapper();
|
||||
final DocumentDbMappingContext mappingContext = new DocumentDbMappingContext();
|
||||
|
||||
|
@ -77,7 +77,7 @@ public class DocumentDbTemplatePartitionIT {
|
|||
|
||||
final MappingDocumentDbConverter dbConverter = new MappingDocumentDbConverter(mappingContext, objectMapper);
|
||||
|
||||
dbTemplate = new DocumentDbTemplate(dbFactory, dbConverter, DB_NAME);
|
||||
dbTemplate = new DocumentDbTemplate(cosmosDbFactory, dbConverter, DB_NAME);
|
||||
collectionName = personInfo.getCollectionName();
|
||||
|
||||
dbTemplate.createCollectionIfNotExists(personInfo);
|
||||
|
@ -121,7 +121,9 @@ public class DocumentDbTemplatePartitionIT {
|
|||
@Test
|
||||
public void testUpsertNewDocumentPartition() {
|
||||
final String firstName = NEW_FIRST_NAME + "_" + UUID.randomUUID().toString();
|
||||
final PartitionPerson newPerson = new PartitionPerson(null, firstName, NEW_LAST_NAME, null, null);
|
||||
final PartitionPerson newPerson = new PartitionPerson(TEST_PERSON.getId(),
|
||||
firstName, NEW_LAST_NAME,
|
||||
null, null);
|
||||
|
||||
final String partitionKeyValue = newPerson.getLastName();
|
||||
dbTemplate.upsert(PartitionPerson.class.getSimpleName(), newPerson, new PartitionKey(partitionKeyValue));
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core;
|
||||
|
||||
import com.microsoft.azure.spring.data.cosmosdb.DocumentDbFactory;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.CosmosDbFactory;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.common.TestConstants;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.config.DocumentDBConfig;
|
||||
import org.junit.Test;
|
||||
|
@ -19,8 +19,8 @@ public class DocumentDbTemplateUnitTest {
|
|||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectNullDbFactory() {
|
||||
final DocumentDBConfig dbConfig = DocumentDBConfig.builder("", "", TestConstants.DB_NAME).build();
|
||||
final DocumentDbFactory dbFactory = new DocumentDbFactory(dbConfig);
|
||||
final CosmosDbFactory cosmosDbFactory = new CosmosDbFactory(dbConfig);
|
||||
|
||||
new DocumentDbTemplate(dbFactory, null, TestConstants.DB_NAME);
|
||||
new DocumentDbTemplate(cosmosDbFactory, null, TestConstants.DB_NAME);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,10 +117,9 @@ public class ReactiveCosmosTemplateIT {
|
|||
public void testFindByID() {
|
||||
final Mono<Person> findById = cosmosTemplate.findById(Person.class.getSimpleName(), TEST_PERSON.getId(),
|
||||
Person.class);
|
||||
StepVerifier.create(findById).consumeNextWith(actual -> {
|
||||
Assert.assertThat(actual.getFirstName(), is(equalTo(TEST_PERSON.getFirstName())));
|
||||
Assert.assertThat(actual.getLastName(), is(equalTo(TEST_PERSON.getLastName())));
|
||||
}).verifyComplete();
|
||||
StepVerifier.create(findById)
|
||||
.consumeNextWith(actual -> Assert.assertEquals(actual, TEST_PERSON))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -143,10 +142,8 @@ public class ReactiveCosmosTemplateIT {
|
|||
@Test
|
||||
public void testFindByIdWithContainerName() {
|
||||
StepVerifier.create(cosmosTemplate.findById(Person.class.getSimpleName(), TEST_PERSON.getId(), Person.class))
|
||||
.consumeNextWith(actual -> {
|
||||
Assert.assertThat(actual.getFirstName(), is(equalTo(TEST_PERSON.getFirstName())));
|
||||
Assert.assertThat(actual.getLastName(), is(equalTo(TEST_PERSON.getLastName())));
|
||||
}).verifyComplete();
|
||||
.consumeNextWith(actual -> Assert.assertEquals(actual, TEST_PERSON))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -202,18 +199,26 @@ public class ReactiveCosmosTemplateIT {
|
|||
@Test
|
||||
public void testDeleteById() {
|
||||
cosmosTemplate.insert(TEST_PERSON_4).block();
|
||||
Flux<Person> flux = cosmosTemplate.findAll(Person.class.getSimpleName(), Person.class);
|
||||
StepVerifier.create(flux).expectNextCount(2).verifyComplete();
|
||||
final Mono<Void> voidMono = cosmosTemplate.deleteById(Person.class.getSimpleName(), TEST_PERSON_4.getId(),
|
||||
new PartitionKey(TEST_PERSON_4.getId()));
|
||||
PartitionKey.None);
|
||||
StepVerifier.create(voidMono).verifyComplete();
|
||||
flux = cosmosTemplate.findAll(Person.class.getSimpleName(), Person.class);
|
||||
StepVerifier.create(flux).expectNextCount(1).verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteByIdBySecondaryKey() {
|
||||
cosmosKeyCredential.key(documentDbSecondaryKey);
|
||||
cosmosTemplate.insert(TEST_PERSON_4).block();
|
||||
Flux<Person> flux = cosmosTemplate.findAll(Person.class.getSimpleName(), Person.class);
|
||||
StepVerifier.create(flux).expectNextCount(2).verifyComplete();
|
||||
final Mono<Void> voidMono = cosmosTemplate.deleteById(Person.class.getSimpleName(), TEST_PERSON_4.getId(),
|
||||
new PartitionKey(TEST_PERSON_4.getId()));
|
||||
PartitionKey.None);
|
||||
StepVerifier.create(voidMono).verifyComplete();
|
||||
flux = cosmosTemplate.findAll(Person.class.getSimpleName(), Person.class);
|
||||
StepVerifier.create(flux).expectNextCount(1).verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -5,14 +5,15 @@
|
|||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core.converter;
|
||||
|
||||
import com.azure.data.cosmos.CosmosItemProperties;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.microsoft.azure.documentdb.Document;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.common.TestConstants;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.convert.MappingDocumentDbConverter;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.mapping.DocumentDbMappingContext;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.domain.Address;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.domain.Memo;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.domain.Importance;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
@ -53,22 +54,23 @@ public class MappingDocumentDbConverterUnitTest {
|
|||
@Test
|
||||
public void covertAddressToDocumentCorrectly() {
|
||||
final Address testAddress = new Address(TestConstants.POSTAL_CODE, TestConstants.CITY, TestConstants.STREET);
|
||||
final Document document = dbConverter.writeDoc(testAddress);
|
||||
final CosmosItemProperties cosmosItemProperties = dbConverter.writeCosmosItemProperties(testAddress);
|
||||
|
||||
assertThat(document.getId()).isEqualTo(testAddress.getPostalCode());
|
||||
assertThat(document.getString(TestConstants.PROPERTY_CITY)).isEqualTo(testAddress.getCity());
|
||||
assertThat(document.getString(TestConstants.PROPERTY_STREET)).isEqualTo(testAddress.getStreet());
|
||||
assertThat(cosmosItemProperties.id()).isEqualTo(testAddress.getPostalCode());
|
||||
assertThat(cosmosItemProperties.getString(TestConstants.PROPERTY_CITY)).isEqualTo(testAddress.getCity());
|
||||
assertThat(cosmosItemProperties.getString(TestConstants.PROPERTY_STREET)).isEqualTo(testAddress.getStreet());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertDocumentToAddressCorrectly() {
|
||||
final Document document = new Document();
|
||||
final JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put(TestConstants.PROPERTY_CITY, TestConstants.CITY);
|
||||
jsonObject.put(TestConstants.PROPERTY_STREET, TestConstants.STREET);
|
||||
|
||||
document.setId(TestConstants.POSTAL_CODE);
|
||||
document.set(TestConstants.PROPERTY_CITY, TestConstants.CITY);
|
||||
document.set(TestConstants.PROPERTY_STREET, TestConstants.STREET);
|
||||
final CosmosItemProperties cosmosItemProperties = new CosmosItemProperties(jsonObject.toString());
|
||||
cosmosItemProperties.id(TestConstants.POSTAL_CODE);
|
||||
|
||||
final Address address = dbConverter.read(Address.class, document);
|
||||
final Address address = dbConverter.read(Address.class, cosmosItemProperties);
|
||||
|
||||
assertThat(address.getPostalCode()).isEqualTo(TestConstants.POSTAL_CODE);
|
||||
assertThat(address.getCity()).isEqualTo(TestConstants.CITY);
|
||||
|
@ -79,26 +81,28 @@ public class MappingDocumentDbConverterUnitTest {
|
|||
public void canWritePojoWithDateToDocument() throws ParseException {
|
||||
final Memo memo = new Memo(TestConstants.ID_1, TestConstants.MESSAGE, DATE.parse(TestConstants.DATE_STRING),
|
||||
Importance.NORMAL);
|
||||
final Document document = dbConverter.writeDoc(memo);
|
||||
final CosmosItemProperties cosmosItemProperties = dbConverter.writeCosmosItemProperties(memo);
|
||||
|
||||
assertThat(document.getId()).isEqualTo(memo.getId());
|
||||
assertThat(document.getString(TestConstants.PROPERTY_MESSAGE)).isEqualTo(memo.getMessage());
|
||||
assertThat(document.getLong(TestConstants.PROPERTY_DATE)).isEqualTo(memo.getDate().getTime());
|
||||
assertThat(cosmosItemProperties.id()).isEqualTo(memo.getId());
|
||||
assertThat(cosmosItemProperties.getString(TestConstants.PROPERTY_MESSAGE)).isEqualTo(memo.getMessage());
|
||||
assertThat(cosmosItemProperties.getLong(TestConstants.PROPERTY_DATE)).isEqualTo(memo.getDate().getTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canReadPojoWithDateFromDocument() throws ParseException {
|
||||
final Document document = new Document();
|
||||
document.setId(TestConstants.ID_1);
|
||||
document.set(TestConstants.PROPERTY_MESSAGE, TestConstants.MESSAGE);
|
||||
final JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put(TestConstants.PROPERTY_MESSAGE, TestConstants.MESSAGE);
|
||||
|
||||
final long date = DATE.parse(TestConstants.DATE_STRING).getTime();
|
||||
document.set(TestConstants.PROPERTY_DATE, date);
|
||||
jsonObject.put(TestConstants.PROPERTY_DATE, date);
|
||||
|
||||
final Memo memo = dbConverter.read(Memo.class, document);
|
||||
assertThat(document.getId()).isEqualTo(memo.getId());
|
||||
assertThat(document.getString(TestConstants.PROPERTY_MESSAGE)).isEqualTo(TestConstants.MESSAGE);
|
||||
assertThat(document.getLong(TestConstants.PROPERTY_DATE)).isEqualTo(date);
|
||||
final CosmosItemProperties cosmosItemProperties = new CosmosItemProperties(jsonObject.toString());
|
||||
cosmosItemProperties.id(TestConstants.ID_1);
|
||||
|
||||
final Memo memo = dbConverter.read(Memo.class, cosmosItemProperties);
|
||||
assertThat(cosmosItemProperties.id()).isEqualTo(memo.getId());
|
||||
assertThat(cosmosItemProperties.getString(TestConstants.PROPERTY_MESSAGE)).isEqualTo(TestConstants.MESSAGE);
|
||||
assertThat(cosmosItemProperties.getLong(TestConstants.PROPERTY_DATE)).isEqualTo(date);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -124,8 +124,7 @@ public class AddressRepositoryIT {
|
|||
|
||||
@Test
|
||||
public void deleteWithoutPartitionedColumnShouldFail() {
|
||||
expectedException.expect(UnsupportedOperationException.class);
|
||||
expectedException.expectMessage("PartitionKey value must be supplied for this operation.");
|
||||
expectedException.expect(Exception.class);
|
||||
|
||||
repository.deleteById(TEST_ADDRESS1_PARTITION1.getPostalCode());
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.assertj.core.util.Lists;
|
|||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -156,6 +157,7 @@ public class ContactRepositoryIT {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore // TODO(kuthapar): v3 doesn't support creation of items without id.
|
||||
public void testNullIdContact() {
|
||||
final Contact nullIdContact = new Contact(null, "testTitile");
|
||||
final Contact savedContact = this.repository.save(nullIdContact);
|
||||
|
|
|
@ -7,7 +7,7 @@ package com.microsoft.azure.spring.data.cosmosdb.repository.integration;
|
|||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.microsoft.azure.documentdb.*;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.DocumentDbFactory;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.CosmosDbFactory;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.common.TestConstants;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.common.TestUtils;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.config.DocumentDBConfig;
|
||||
|
@ -62,7 +62,7 @@ public class DocumentDBAnnotationIT {
|
|||
@Before
|
||||
public void setUp() throws ClassNotFoundException {
|
||||
final DocumentDBConfig dbConfig = DocumentDBConfig.builder(dbUri, dbKey, TestConstants.DB_NAME).build();
|
||||
final DocumentDbFactory dbFactory = new DocumentDbFactory(dbConfig);
|
||||
final CosmosDbFactory cosmosDbFactory = new CosmosDbFactory(dbConfig);
|
||||
|
||||
roleInfo = new DocumentDbEntityInformation<>(Role.class);
|
||||
sampleInfo = new DocumentDbEntityInformation<>(TimeToLiveSample.class);
|
||||
|
@ -73,7 +73,7 @@ public class DocumentDBAnnotationIT {
|
|||
|
||||
mappingConverter = new MappingDocumentDbConverter(dbContext, objectMapper);
|
||||
dbClient = new DocumentClient(dbUri, dbKey, ConnectionPolicy.GetDefault(), ConsistencyLevel.Session);
|
||||
dbTemplate = new DocumentDbTemplate(dbFactory, mappingConverter, TestConstants.DB_NAME);
|
||||
dbTemplate = new DocumentDbTemplate(cosmosDbFactory, mappingConverter, TestConstants.DB_NAME);
|
||||
|
||||
collectionRole = dbTemplate.createCollectionIfNotExists(roleInfo);
|
||||
collectionExample = dbTemplate.createCollectionIfNotExists(sampleInfo);
|
||||
|
@ -103,6 +103,7 @@ public class DocumentDBAnnotationIT {
|
|||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
@Ignore // TODO(kuthapar): time to live is not supported by v3 SDK.
|
||||
public void testDocumentAnnotationTimeToLive() {
|
||||
final TimeToLiveSample sample = new TimeToLiveSample(TestConstants.ID_1);
|
||||
final Integer timeToLive = this.collectionExample.getDefaultTimeToLive();
|
||||
|
|
|
@ -7,7 +7,6 @@ package com.microsoft.azure.spring.data.cosmosdb.repository.integration;
|
|||
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.DocumentDbTemplate;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.domain.Question;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.exception.DocumentDBAccessException;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.repository.TestRepositoryConfig;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.repository.repository.ProjectRepository;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.repository.repository.QuestionRepository;
|
||||
|
@ -72,9 +71,10 @@ public class QuestionRepositoryIT {
|
|||
Assert.assertEquals(QUESTION, optional.get());
|
||||
}
|
||||
|
||||
@Test(expected = DocumentDBAccessException.class)
|
||||
public void testFindByIdException() {
|
||||
this.repository.findById(QUESTION_URL);
|
||||
@Test
|
||||
public void testFindByIdNull() {
|
||||
final Optional<Question> byId = this.repository.findById(QUESTION_URL);
|
||||
Assert.assertFalse(byId.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Загрузка…
Ссылка в новой задаче