Moved spring-data-cosmosdb repo to new repo
This commit is contained in:
Родитель
f62cf3670b
Коммит
7acfde5db9
|
@ -1,3 +0,0 @@
|
|||
exclude_paths:
|
||||
- 'src/test/java/com/microsoft/azure/spring/data/cosmosdb/domain/Person.java'
|
||||
- 'src/test/java/com/microsoft/azure/spring/data/cosmosdb/repository/support/CosmosEntityInformationUnitTest.java'
|
|
@ -1,45 +0,0 @@
|
|||
# Compiled class file
|
||||
*.class
|
||||
.classpath
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
# IntelliJ
|
||||
.idea
|
||||
*.iml
|
||||
.project
|
||||
.settings
|
||||
|
||||
# Eclipse
|
||||
.metadata
|
||||
.checkstyle
|
||||
.fbExcludeFilterFile
|
||||
|
||||
#Spring
|
||||
.springBeans
|
||||
.factorypath
|
||||
|
||||
# Maven
|
||||
target
|
||||
/bin/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
*.dll
|
29
.travis.yml
29
.travis.yml
|
@ -1,29 +0,0 @@
|
|||
language: java
|
||||
jdk:
|
||||
- openjdk8
|
||||
|
||||
sudo: required
|
||||
install: true
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- stage: test
|
||||
script:
|
||||
- if [ -z "$CLIENT_ID" ]; then travis_terminate 0; fi
|
||||
- sudo apt-get install python3.5 python3.5-dev
|
||||
- curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
|
||||
- sudo apt-get update
|
||||
- sudo apt-get install curl apt-transport-https lsb-release gnupg
|
||||
- curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc.gpg > /dev/null
|
||||
- AZ_REPO=$(lsb_release -cs) && echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ $AZ_REPO main" | sudo tee /etc/apt/sources.list.d/azure-cli.list
|
||||
- sudo apt-get update
|
||||
- sudo apt-get install azure-cli
|
||||
# - az --help
|
||||
# - chmod +x install.sh && ./install.sh
|
||||
- chmod +x ./src/libs/travis-wait-mvn-build.sh && ./src/libs/travis-wait-mvn-build.sh
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
- stage: performance-test
|
||||
script:
|
||||
- mvn -P performance-test clean verify
|
||||
if: branch = master AND type != pull_request
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
# How to Build and Contribute
|
||||
This instruction is guideline for building and code contribution.
|
||||
|
||||
## Prequisites
|
||||
- JDK 1.8 and above
|
||||
- [Maven](http://maven.apache.org/) 3.0 and above
|
||||
|
||||
## Build from source
|
||||
To build the project, run maven commands.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/Microsoft/spring-data-cosmosdb.git
|
||||
cd spring-data-cosmosdb
|
||||
mvnw clean install
|
||||
```
|
||||
|
||||
## Test
|
||||
There're 3 profiles: `dev`, `integration-test-azure` and `integration-test-emulator`. Default profile is `dev`. Profile `integration-test-azure` will trigger integration test execution against Azure Cosmos DB. Profile `integration-test-emulator` will trigger integration test execution against [Azure Cosmos DB Emulator](https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator), you need to follow the link to setup emulator before test execution.
|
||||
|
||||
- Run unit tests
|
||||
```bash
|
||||
mvnw clean install
|
||||
```
|
||||
|
||||
- Run integration tests
|
||||
- on Azure
|
||||
>**NOTE** Please note that integration test against Azure will automatically create a Azure Cosmos DB Document API in your Azure subscription, then there will be **Azure usage fee.**
|
||||
|
||||
Integration tests will require a Azure Subscription. If you don't already have an Azure subscription, you can activate your [MSDN subscriber benefits](https://azure.microsoft.com/en-us/pricing/member-offers/msdn-benefits-details/) or sign up for a [free Azure account](https://azure.microsoft.com/en-us/free/).
|
||||
|
||||
1. Create a service principal by using Azure Cli or by [Azure Portal](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal).
|
||||
2. After service principal ready, set environment variables CLIENT_ID, CLIENT_KEY and TENANT_ID, where value of them are service principal id, key and tenant id.
|
||||
3. Run maven command with `integration-test-azure` profile.
|
||||
|
||||
```bash
|
||||
set CLIENT_ID=your-azure-service-principal-id
|
||||
set CLIENT_KEY=your-azure-service-principal-key
|
||||
set TENANT_ID=your-azure-subscription-tenant-id
|
||||
mvnw -P integration-test-azure clean install
|
||||
```
|
||||
- on Emulator
|
||||
|
||||
Setup Azure Cosmos DB Emulator by following [this instruction](https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator), then run test with:
|
||||
```bash
|
||||
mvnw -P integration-test-emulator install
|
||||
```
|
||||
|
||||
|
||||
- Skip tests execution
|
||||
```bash
|
||||
mvnw clean install -DskipTests
|
||||
```
|
||||
|
||||
## Version management
|
||||
Developing version naming convention is like `0.1.2-SNAPSHOT`. Release version naming convention is like `0.1.2`.
|
||||
|
||||
## CI
|
||||
Both [travis](https://travis-ci.org/Microsoft/spring-data-cosmosdb) and [appveyor](https://ci.appveyor.com/project/yungez/spring-data-cosmosdb) CI is enabled.
|
||||
|
||||
## Contribute to code
|
||||
Code contribution is welcome. To contribute to this module, please make sure below check list are checked.
|
||||
- [ ] Build pass. checkstyle and findbugs is enabled by default. Please check [checkstyle.xml](config/checkstyle.xml) to learn detail checkstyle configuration.
|
||||
- [ ] Documents are updated to align with code.
|
||||
- [ ] Code coverage for new codes >= 65%. Code coverage check is enabled with 65% bar.
|
|
@ -1,58 +0,0 @@
|
|||
### How to Query Partitioned Azure Cosmos DB Collection
|
||||
|
||||
With Azure Cosmos DB, you can configure [partition key](https://docs.microsoft.com/en-us/azure/cosmos-db/partition-data) for your collection.
|
||||
|
||||
Below is an example about how to query partitioned collection with this spring data module.
|
||||
|
||||
#### Example
|
||||
|
||||
Given a document entity structure:
|
||||
```
|
||||
@Document
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class Address {
|
||||
@Id
|
||||
String postalCode;
|
||||
String street;
|
||||
@PartitionKey
|
||||
String city;
|
||||
}
|
||||
```
|
||||
|
||||
Write the repository interface:
|
||||
```
|
||||
@Repository
|
||||
public interface AddressRepository extends DocumentDbRepository<Address, String> {
|
||||
// Add query methods here, refer to below
|
||||
}
|
||||
```
|
||||
|
||||
Query by field name:
|
||||
```
|
||||
List<Address> findByCity(String city);
|
||||
```
|
||||
|
||||
Delete by field name:
|
||||
```
|
||||
void deleteByStreet(String street);
|
||||
```
|
||||
|
||||
For `Partitioned collection`, if you want to query records by `findById(id)`, exception will be thrown.
|
||||
```
|
||||
// Incorrect for partitioned collection, exception will be thrown
|
||||
Address result = repository.findById(id); // Caution: Works for non-partitioned collection
|
||||
```
|
||||
|
||||
Instead, you can query records by ID field name with custom query.
|
||||
```
|
||||
// Correct, postalCode is the ID field in Address domain
|
||||
@Repository
|
||||
public interface AddressRepository extends DocumentDbRepository<Address, String> {
|
||||
List<Address> findByPostalCode(String postalCode);
|
||||
}
|
||||
|
||||
// Query
|
||||
List<Address> result = repository.findByPostalCode(postalCode);
|
||||
```
|
||||
|
335
README.md
335
README.md
|
@ -1,335 +1,8 @@
|
|||
[![Travis CI](https://travis-ci.org/Microsoft/spring-data-cosmosdb.svg?branch=master)](https://travis-ci.org/Microsoft/spring-data-cosmosdb)
|
||||
[![codecov](https://codecov.io/gh/Microsoft/spring-data-cosmosdb/branch/master/graph/badge.svg)](https://codecov.io/gh/Microsoft/spring-data-cosmosdb)
|
||||
[![MIT License](http://img.shields.io/badge/license-MIT-green.svg) ](https://github.com/Microsoft/spring-data-cosmosdb/blob/master/LICENSE)
|
||||
|
||||
|
||||
# Spring Data for Azure Cosmos DB
|
||||
|
||||
[Azure Cosmos DB](https://docs.microsoft.com/en-us/azure/cosmos-db/introduction) is a globally-distributed database service that allows developers to work with data using a variety of standard APIs, such as SQL, MongoDB, Cassandra, Graph, and Table.
|
||||
## Spring Data CosmosDb has been moved to new repo - [Azure-sdk-for-java](https://github.com/Azure/azure-sdk-for-java)
|
||||
## New location for spring-data-cosmsodb source code is here [azure-sdk-for-java/sdk/cosmos/azure-spring-data-cosmos](https://github.com/Azure/azure-sdk-for-java/tree/master/sdk/cosmos/azure-spring-data-cosmos)
|
||||
|
||||
**Spring Data Azure Cosmos DB** provides initial Spring Data support for Azure Cosmos DB using the [SQL API](https://docs.microsoft.com/en-us/azure/cosmos-db/sql-api-introduction), based on Spring Data framework. Currently it only supports SQL API, the other APIs are in the plan.
|
||||
## We will continue to respond to open issues here, but new issues should be created in the new repo - [Azure-sdk-for-java](https://github.com/Azure/azure-sdk-for-java)
|
||||
|
||||
## TOC
|
||||
|
||||
* [Sample Code](#sample-code)
|
||||
* [Spring data version support](#spring-data-version-support)
|
||||
* [Feature List](#feature-list)
|
||||
* [Quick Start](#quick-start)
|
||||
* [Query Partitioned Collection](QueryPartitionedCollection.md)
|
||||
* [Snapshots](#snapshots)
|
||||
* [Filing Issues](#filing-issues)
|
||||
* [How to Contribute](#how-to-contribute)
|
||||
* [Code of Conduct](#code-of-conduct)
|
||||
|
||||
## Sample Code
|
||||
Please refer to [sample project here](./samplecode).
|
||||
|
||||
## Spring Data Version Support
|
||||
Version mapping between spring boot and spring-data-cosmosdb:
|
||||
|
||||
| Spring boot version | spring-data-cosmosdb version |
|
||||
| :----------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
|
||||
| ![version](https://img.shields.io/badge/version-2.3.x-blue) | [![Maven Central](https://img.shields.io/maven-central/v/com.microsoft.azure/spring-data-cosmosdb/2.3.svg)](https://search.maven.org/search?q=g:com.microsoft.azure%20AND%20a:spring-data-cosmosdb%20AND%20v:2.3.*) |
|
||||
| ![version](https://img.shields.io/badge/version-2.2.x-blue) | [![Maven Central](https://img.shields.io/maven-central/v/com.microsoft.azure/spring-data-cosmosdb/2.2.svg)](https://search.maven.org/search?q=g:com.microsoft.azure%20AND%20a:spring-data-cosmosdb%20AND%20v:2.2.*) |
|
||||
| ![version](https://img.shields.io/badge/version-2.1.x-blue) | [![Maven Central](https://img.shields.io/maven-central/v/com.microsoft.azure/spring-data-cosmosdb/2.1.svg)](https://search.maven.org/search?q=g:com.microsoft.azure%20AND%20a:spring-data-cosmosdb%20AND%20v:2.1.*) |
|
||||
| ![version](https://img.shields.io/badge/version-2.0.x-blue) | [![Maven Central](https://img.shields.io/maven-central/v/com.microsoft.azure/spring-data-cosmosdb/2.0.svg)](https://search.maven.org/search?q=g:com.microsoft.azure%20AND%20a:spring-data-cosmosdb%20AND%20v:2.0.*) |
|
||||
|
||||
## Feature List
|
||||
- Spring Data ReactiveCrudRepository CrudRepository basic CRUD functionality
|
||||
- save
|
||||
- findAll
|
||||
- findOne by Id
|
||||
- deleteAll
|
||||
- delete by Id
|
||||
- delete entity
|
||||
- Spring Data [@Id](https://github.com/spring-projects/spring-data-commons/blob/db62390de90c93a78743c97cc2cc9ccd964994a5/src/main/java/org/springframework/data/annotation/Id.java) annotation.
|
||||
There're 2 ways to map a field in domain class to `id` field of Azure Cosmos DB document.
|
||||
- annotate a field in domain class with `@Id`, this field will be mapped to document `id` in Cosmos DB.
|
||||
- set name of this field to `id`, this field will be mapped to document `id` in Azure Cosmos DB.
|
||||
- Custom collection Name.
|
||||
By default, collection name will be class name of user domain class. To customize it, add the `@Document(collection="myCustomCollectionName")` annotation to the domain class. The collection field also supports SpEL expressions (eg. `collection = "${dynamic.collection.name}"` or `collection = "#{@someBean.getContainerName()}"`) in order to provide collection names programmatically/via configuration properties.
|
||||
- Custom IndexingPolicy
|
||||
By default, IndexingPolicy will be set by azure service. To customize it add annotation `@DocumentIndexingPolicy` to domain class. This annotation has 4 attributes to customize, see following:
|
||||
```java
|
||||
boolean automatic; // Indicate if indexing policy use automatic or not
|
||||
IndexingMode mode; // Indexing policy mode, option Consistent|Lazy|None.
|
||||
String[] includePaths; // Included paths for indexing
|
||||
String[] excludePaths; // Excluded paths for indexing
|
||||
```
|
||||
- Supports Optimistic Locking for specific collections, which means upserts/deletes by document will fail with an exception in case the document was modified by another process in the meanwhile. To enable Optimistic Locking for a collection, just create a string `_etag` field and mark it with the `@Version` annotation. See the following:
|
||||
|
||||
```java
|
||||
@Document(collection = "myCollection")
|
||||
class MyDocument {
|
||||
String id;
|
||||
String data;
|
||||
@Version
|
||||
String _etag;
|
||||
}
|
||||
```
|
||||
- Supports [Azure Cosmos DB partition](https://docs.microsoft.com/en-us/azure/cosmos-db/partition-data). To specify a field of domain class to be partition key field, just annotate it with `@PartitionKey`. When you do CRUD operation, pls specify your partition value. For more sample on partition CRUD, pls refer to [test here](./src/test/java/com/microsoft/azure/spring/data/cosmosdb/repository/integration/AddressRepositoryIT.java)
|
||||
- Supports [Spring Data custom query](https://docs.spring.io/spring-data/commons/docs/current/reference/html/#repositories.query-methods.details) find operation, e.g., `findByAFieldAndBField`
|
||||
- Supports [Spring Data pagable and sort](https://docs.spring.io/spring-data/commons/docs/current/reference/html/#repositories.special-parameters).
|
||||
- Based on available RUs on the database account, cosmosDB can return documents less than or equal to the requested size.
|
||||
- Due to this variable number of returned documents in every iteration, user should not rely on the totalPageSize, and instead iterating over pageable should be done in this way.
|
||||
```java
|
||||
final CosmosPageRequest pageRequest = new CosmosPageRequest(0, pageSize, null);
|
||||
Page<T> page = tRepository.findAll(pageRequest);
|
||||
List<T> pageContent = page.getContent();
|
||||
while(page.hasNext()) {
|
||||
Pageable nextPageable = page.nextPageable();
|
||||
page = repository.findAll(nextPageable);
|
||||
pageContent = page.getContent();
|
||||
}
|
||||
```
|
||||
- Supports [spring-boot-starter-data-rest](https://projects.spring.io/spring-data-rest/).
|
||||
- Supports List and nested type in domain class.
|
||||
- Configurable ObjectMapper bean with unique name `cosmosdbObjectMapper`, only configure customized ObjectMapper if you really need to. e.g.,
|
||||
```java
|
||||
@Bean(name = "cosmosdbObjectMapper")
|
||||
public ObjectMapper objectMapper() {
|
||||
return new ObjectMapper(); // Do configuration to the ObjectMapper if required
|
||||
}
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Add the dependency
|
||||
`spring-data-cosmosdb` is published on Maven Central Repository.
|
||||
If you are using Maven, add the following dependency.
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.microsoft.azure</groupId>
|
||||
<artifactId>spring-data-cosmosdb</artifactId>
|
||||
<version>2.2.4</version>
|
||||
</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.
|
||||
|
||||
### Sync and Reactive Repository support
|
||||
2.2.x supports both sync and reactive repository support.
|
||||
|
||||
Use `@EnableCosmosRepositories` to enable sync repository support.
|
||||
|
||||
For reactive repository support, use `@EnableReactiveCosmosRepositories`
|
||||
|
||||
### Response Diagnostics String and Query Metrics
|
||||
2.2.x supports Response Diagnostics String and Query Metrics.
|
||||
Set `populateQueryMetrics` flag to true in application.properties to enable query metrics.
|
||||
In addition to setting the flag, implement `ResponseDiagnosticsProcessor` to log diagnostics information.
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
@EnableCosmosRepositories
|
||||
@Slf4j
|
||||
public class AppConfiguration extends AbstractCosmosConfiguration {
|
||||
|
||||
@Value("${azure.cosmosdb.uri}")
|
||||
private String uri;
|
||||
|
||||
@Value("${azure.cosmosdb.key}")
|
||||
private String key;
|
||||
|
||||
@Value("${azure.cosmosdb.secondaryKey}")
|
||||
private String secondaryKey;
|
||||
|
||||
@Value("${azure.cosmosdb.database}")
|
||||
private String dbName;
|
||||
|
||||
@Value("${azure.cosmosdb.populateQueryMetrics}")
|
||||
private boolean populateQueryMetrics;
|
||||
|
||||
private CosmosKeyCredential cosmosKeyCredential;
|
||||
|
||||
public CosmosDBConfig getConfig() {
|
||||
this.cosmosKeyCredential = new CosmosKeyCredential(key);
|
||||
CosmosDbConfig cosmosdbConfig = CosmosDBConfig.builder(uri,
|
||||
this.cosmosKeyCredential, dbName).build();
|
||||
cosmosdbConfig.setPopulateQueryMetrics(populateQueryMetrics);
|
||||
cosmosdbConfig.setResponseDiagnosticsProcessor(new ResponseDiagnosticsProcessorImplementation());
|
||||
return cosmosdbConfig;
|
||||
}
|
||||
|
||||
public void switchToSecondaryKey() {
|
||||
this.cosmosKeyCredential.key(secondaryKey);
|
||||
}
|
||||
|
||||
private static class ResponseDiagnosticsProcessorImplementation implements ResponseDiagnosticsProcessor {
|
||||
|
||||
@Override
|
||||
public void processResponseDiagnostics(@Nullable ResponseDiagnostics responseDiagnostics) {
|
||||
log.info("Response Diagnostics {}", responseDiagnostics);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
Or if you want to customize your config:
|
||||
```java
|
||||
public CosmosDBConfig getConfig() {
|
||||
this.cosmosKeyCredential = new CosmosKeyCredential(key);
|
||||
CosmosDBConfig cosmosDbConfig = CosmosDBConfig.builder(uri, this.cosmosKeyCredential, dbName).build();
|
||||
cosmosDbConfig.getConnectionPolicy().setConnectionMode(ConnectionMode.DIRECT);
|
||||
cosmosDbConfig.getConnectionPolicy().setMaxPoolSize(1000);
|
||||
return cosmosDbConfig;
|
||||
}
|
||||
```
|
||||
By default, `@EnableCosmosRepositories` will scan the current package for any interfaces that extend one of Spring Data's repository interfaces. Using it to annotate your Configuration class to scan a different root package by type if your project layout has multiple projects and it's not finding your repositories.
|
||||
```java
|
||||
@Configuration
|
||||
@EnableCosmosRepositories(basePackageClass=UserRepository.class)
|
||||
public class AppConfiguration extends AbstractCosmosConfiguration {
|
||||
// configuration code
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Define an entity
|
||||
Define a simple entity as Document in Azure Cosmos DB.
|
||||
|
||||
You can define entities by adding the `@Document` annotation and specifying properties related to the container, such as the container name, request units (RUs), time to live, and auto-create container.
|
||||
|
||||
Containers are created automatically unless you don't want them to: Set `autoCreateCollection` to false in `@Document` annotation to disable auto creation of containers.
|
||||
|
||||
Note: By default request units assigned to newly created containers is 4000. Specify different ru value to customize request units for container created by the SDK (minimum RU value is 400).
|
||||
|
||||
```java
|
||||
@Document(collection = "myCollection", ru = "400")
|
||||
public class User {
|
||||
private String id;
|
||||
private String firstName;
|
||||
|
||||
@PartitionKey
|
||||
private String lastName;
|
||||
|
||||
... // setters and getters
|
||||
|
||||
public User() {
|
||||
// If you do not want to create a default constructor,
|
||||
// use annotation @JsonCreator and @JsonProperty in the full args constructor
|
||||
}
|
||||
|
||||
public User(String id, String firstName, String lastName) {
|
||||
this.id = id;
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("User: %s %s, %s", firstName, lastName, id);
|
||||
}
|
||||
}
|
||||
```
|
||||
`id` field will be used as document id in Azure Cosmos DB. If you want use another field like `emailAddress` as document `id`, just annotate that field with `@Id` annotation.
|
||||
|
||||
Annotation `@Document(collection="mycollection")` is used to specify collection name in Azure Cosmos DB.
|
||||
Annotation `@PartitionKey` on `lastName` field is used to specify this field be partition key in Azure Cosmos DB.
|
||||
|
||||
```java
|
||||
@Document(collection = "mycollection")
|
||||
public class User {
|
||||
@Id
|
||||
private String emailAddress;
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Create repositories
|
||||
Extends CosmosRepository interface, which provides Spring Data repository support.
|
||||
|
||||
```java
|
||||
import CosmosRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface UserRepository extends CosmosRepository<User, String> {
|
||||
List<User> findByFirstName(String firstName);
|
||||
}
|
||||
```
|
||||
|
||||
`findByFirstName` method is custom query method, it will find documents per FirstName.
|
||||
|
||||
### Create an Application class
|
||||
Here create an application class with all the components
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
public class SampleApplication implements CommandLineRunner {
|
||||
|
||||
@Autowired
|
||||
private UserRepository repository;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SampleApplication.class, args);
|
||||
}
|
||||
|
||||
public void run(String... var1) throws Exception {
|
||||
|
||||
final User testUser = new User("testId", "testFirstName", "testLastName");
|
||||
|
||||
repository.deleteAll();
|
||||
repository.save(testUser);
|
||||
|
||||
// to find by Id, please specify partition key value if collection is partitioned
|
||||
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);
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
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.
|
||||
|
||||
## Snapshots
|
||||
[![Nexus OSS](https://img.shields.io/nexus/snapshots/https/oss.sonatype.org/com.microsoft.azure/spring-data-cosmosdb.svg)](https://oss.sonatype.org/content/repositories/snapshots/com/microsoft/azure/spring-data-cosmosdb/)
|
||||
|
||||
Snapshots built from `master` branch are available, add [maven repositories](https://maven.apache.org/settings.html#Repositories) configuration to your pom file as below.
|
||||
```xml
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>nexus-snapshots</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
<updatePolicy>always</updatePolicy>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
```
|
||||
|
||||
## Filing Issues
|
||||
|
||||
If you encounter any bug, please file an issue [here](https://github.com/Microsoft/spring-data-cosmosdb/issues/new).
|
||||
|
||||
To suggest a new feature or changes that could be made, file an issue the same way you would for a bug.
|
||||
|
||||
## How To Contribute
|
||||
|
||||
Contribution is welcome. Please follow [this instruction](./HowToContribute.md) to contribute code.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
### Data/Telemetry
|
||||
|
||||
This project collects usage data and sends it to Microsoft to help improve our products and services. Read our [privacy](https://privacy.microsoft.com/en-us/privacystatement) statement to learn more.
|
||||
### Thank you for your patience. We look forward to continuing to work together with you.
|
39
appveyor.yml
39
appveyor.yml
|
@ -1,39 +0,0 @@
|
|||
image: Visual Studio 2017
|
||||
|
||||
environment:
|
||||
JAVA_HOME: "C:\\Program Files\\Java\\jdk1.8.0"
|
||||
PYTHON: "C:\\Python35"
|
||||
CODECOV_UPLOAD_TOKEN:
|
||||
secure: oQzRyhqqs8c0TeJxPhCxk++H4plFaX6dWZ2I3Q/wtvpNIzNUn9nzPGJbxxjgW2Q7
|
||||
|
||||
install:
|
||||
- cmd: python -m pip install codecov
|
||||
- cmd: copy "C:\Program Files (x86)\Apache\Maven\bin\mvn.cmd" "C:\Program Files (x86)\Apache\Maven\bin\mvn.bat"
|
||||
- cmd: net share "c=c:"
|
||||
# 1. starter docker
|
||||
- ps: Start-Process "C:\Program Files\Docker\dockerd.exe"
|
||||
# 2. starter emulator
|
||||
- cmd: docker pull microsoft/azure-cosmosdb-emulator
|
||||
- cmd: md %LOCALAPPDATA%\CosmosDBEmulatorCert 2>nul
|
||||
- cmd: docker run -d -v %LOCALAPPDATA%\CosmosDBEmulatorCert:c:\CosmosDBEmulator\CosmosDBEmulatorCert -p 8081:8081 -t -i microsoft/azure-cosmosdb-emulator >> dockerid.txt
|
||||
- cmd: set /P dockerid=<dockerid.txt
|
||||
- cmd: timeout 100
|
||||
- cmd: docker logs %dockerid% >> dockerlog.txt
|
||||
- ps: $substtring = type dockerlog.txt | where { $_ -match "\bhttps://[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:8081/$" } | foreach { $matches[0] }; $substtring | Out-File -filepath documentdburi.txt -encoding ASCII
|
||||
- cmd: set /P DOCUMENTDB_URI=< documentdburi.txt
|
||||
# 3. import cert
|
||||
- ps: cd C:\Users\appveyor\AppData\Local\CosmosDBEmulatorCert; .\importcert.ps1
|
||||
# 4. export cert to java cert store
|
||||
- ps: $cert = Get-ChildItem cert:\LocalMachine\my | Where-Object { $_.FriendlyName -eq "DocumentDbEmulatorCertificate"}; export-Certificate -filepath c:\documentdb.cer -cert ${cert} -type CERT -NoClobber
|
||||
- cmd: cd "C:\Program Files\Java\jdk1.8.0\jre\lib\security"
|
||||
- cmd: certutil -encode c:\documentdb.cer documentdbemulatordb.cer
|
||||
- cmd: keytool -keystore cacerts -importcert -alias documentdbemulatorcert -file .\documentdbemulatordb.cer -storepass changeit -trustcacerts -noprompt
|
||||
- cmd: cd "C:\projects\spring-data-cosmosdb"
|
||||
|
||||
|
||||
build_script:
|
||||
- mvn clean install -B -V
|
||||
|
||||
after_build:
|
||||
- mvn -P integration-test-emulator cobertura:cobertura-integration-test
|
||||
- ps: Get-ChildItem -Path . -Recurse -File -Filter coverage.xml | Foreach-Object { codecov -f $_.FullName -t $Env:CODECOV_UPLOAD_TOKEN -X gcov }
|
42
build.xml
42
build.xml
|
@ -1,42 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<project name="test resource prepration and cleanup">
|
||||
<target name="setup">
|
||||
<echo message="setup test resource"/>
|
||||
<exec osfamily="windows" executable="cmd.exe">
|
||||
<arg value="/c"/>
|
||||
<arg value=".\src\libs\setup.bat"/>
|
||||
<arg value="${azure.test.resourcegroup}"/>
|
||||
<arg value="${azure.test.dbname}"/>
|
||||
<redirector outputproperty="cosmosdb.keys"/>
|
||||
</exec>
|
||||
<exec osfamily="unix" executable="bash">
|
||||
<arg value="./src/libs/setup.sh"/>
|
||||
<arg value="${azure.test.resourcegroup}"/>
|
||||
<arg value="${azure.test.dbname}"/>
|
||||
<redirector outputproperty="cosmosdb.keys"/>
|
||||
</exec>
|
||||
<script language="javascript">
|
||||
project.setProperty('cosmosdb.key', project.getProperty('cosmosdb.keys').split(' ')[0]);
|
||||
project.setProperty('cosmosdb.secondaryKey', project.getProperty('cosmosdb.keys').split(' ')[1]);
|
||||
</script>
|
||||
<propertyfile file="./src/test/resources/application.properties">
|
||||
<entry key="cosmosdb.key" value="${cosmosdb.key}"/>
|
||||
<entry key="cosmosdb.secondaryKey" value="${cosmosdb.secondaryKey}"/>
|
||||
<entry key="cosmosdb.uri" value="https://${azure.test.dbname}.documents.azure.com:443"/>
|
||||
</propertyfile>
|
||||
</target>
|
||||
<target name="cleanup">
|
||||
<echo message="cleanup test resource"/>
|
||||
<exec osfamily="windows" executable="cmd.exe">
|
||||
<arg value="/c"/>
|
||||
<arg value=".\src\libs\cleanup.bat"/>
|
||||
<arg value="${azure.test.resourcegroup}"/>
|
||||
<arg value="${azure.test.dbname}"/>
|
||||
</exec>
|
||||
<exec osfamily="unix" executable="bash">
|
||||
<arg value="./src/libs/cleanup.sh"/>
|
||||
<arg value="${azure.test.resourcegroup}"/>
|
||||
<arg value="${azure.test.dbname}"/>
|
||||
</exec>
|
||||
</target>
|
||||
</project>
|
|
@ -1,296 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
|
||||
|
||||
<!-- This is a checkstyle configuration file. For descriptions of
|
||||
what the following rules do, please see the checkstyle configuration
|
||||
page at http://checkstyle.sourceforge.net/config.html -->
|
||||
|
||||
<module name="Checker">
|
||||
|
||||
<module name="SuppressionFilter">
|
||||
</module>
|
||||
|
||||
<module name="Header">
|
||||
<property name="header"
|
||||
value="/**\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See LICENSE in the project root for\n * license information.\n */"/>
|
||||
<property name="fileExtensions" value="java"/>
|
||||
</module>
|
||||
|
||||
<module name="FileTabCharacter">
|
||||
<!-- Checks that there are no tab characters in the file.
|
||||
-->
|
||||
</module>
|
||||
|
||||
<module name="NewlineAtEndOfFile">
|
||||
<property name="lineSeparator" value="lf"/>
|
||||
</module>
|
||||
|
||||
<module name="RegexpSingleline">
|
||||
<!-- Checks that FIXME is not used in comments. TODO is preferred.
|
||||
-->
|
||||
<property name="format" value="((//.*)|(\*.*))FIXME"/>
|
||||
<property name="message"
|
||||
value='TODO is preferred to FIXME. e.g. "TODO(johndoe): Refactor when v2 is released."'/>
|
||||
</module>
|
||||
|
||||
<module name="RegexpSingleline">
|
||||
<!-- Checks that TODOs are named. (Actually, just that they are followed
|
||||
by an open paren.)
|
||||
-->
|
||||
<property name="format" value="((//.*)|(\*.*))TODO [^(]"/>
|
||||
<property name="message"
|
||||
value='All TODOs should be named. e.g. "TODO (johndoe): Refactor when v2 is released."'/>
|
||||
</module>
|
||||
|
||||
<!-- All Java AST specific tests live under TreeWalker module. -->
|
||||
<module name="TreeWalker">
|
||||
|
||||
<!--
|
||||
|
||||
IMPORT CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<module name="RedundantImport">
|
||||
<!-- Checks for redundant import statements. -->
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<!--
|
||||
|
||||
NAMING CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<!-- Item 38 - Adhere to generally accepted naming conventions -->
|
||||
|
||||
<module name="PackageName">
|
||||
<!-- Validates identifiers for package names against the
|
||||
supplied expression. -->
|
||||
<!-- Here the default checkstyle rule restricts package name parts to
|
||||
seven characters, this is not in line with common practice at Google.
|
||||
-->
|
||||
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]{1,})*$"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="TypeNameCheck">
|
||||
<!-- Validates static, final fields against the
|
||||
expression "^[A-Z][a-zA-Z0-9]*$". -->
|
||||
<metadata name="altname" value="TypeName"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="ConstantNameCheck">
|
||||
<!-- Validates non-private, static, final fields against the supplied
|
||||
public/package final fields "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$". -->
|
||||
<metadata name="altname" value="ConstantName"/>
|
||||
<property name="applyToPublic" value="true"/>
|
||||
<property name="applyToProtected" value="true"/>
|
||||
<property name="applyToPackage" value="true"/>
|
||||
<property name="applyToPrivate" value="false"/>
|
||||
<property name="format" value="^([A-Z][A-Z0-9]*(_[A-Z0-9]+)*|FLAG_.*)$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Variable ''{0}'' should be in ALL_CAPS (if it is a constant) or be private (otherwise)."/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="StaticVariableNameCheck">
|
||||
<!-- Validates static, non-final fields against the supplied
|
||||
expression "^[a-z][a-zA-Z0-9]*_?$". -->
|
||||
<metadata name="altname" value="StaticVariableName"/>
|
||||
<property name="applyToPublic" value="true"/>
|
||||
<property name="applyToProtected" value="true"/>
|
||||
<property name="applyToPackage" value="true"/>
|
||||
<property name="applyToPrivate" value="true"/>
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*_?$"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="MemberNameCheck">
|
||||
<!-- Validates non-static members against the supplied expression. -->
|
||||
<metadata name="altname" value="MemberName"/>
|
||||
<property name="applyToPublic" value="true"/>
|
||||
<property name="applyToProtected" value="true"/>
|
||||
<property name="applyToPackage" value="true"/>
|
||||
<property name="applyToPrivate" value="true"/>
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="MethodNameCheck">
|
||||
<!-- Validates identifiers for method names. -->
|
||||
<metadata name="altname" value="MethodName"/>
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*(_[a-zA-Z0-9]+)*$"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="ParameterName">
|
||||
<!-- Validates identifiers for method parameters against the
|
||||
expression "^[a-z][a-zA-Z0-9]*$". -->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="LocalFinalVariableName">
|
||||
<!-- Validates identifiers for local final variables against the
|
||||
expression "^[a-z][a-zA-Z0-9]*$". -->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="LocalVariableName">
|
||||
<!-- Validates identifiers for local variables against the
|
||||
expression "^[a-z][a-zA-Z0-9]*$". -->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="FinalLocalVariable">
|
||||
<!-- Checks that local variables that never have their values changed are declared final. -->
|
||||
<property name="tokens" value="VARIABLE_DEF"/>
|
||||
<property name="validateEnhancedForLoopVariable" value="true"/>
|
||||
</module>
|
||||
|
||||
<!--
|
||||
|
||||
LENGTH and CODING CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<module name="LineLength">
|
||||
<!-- Checks if a line is too long. -->
|
||||
<property name="max" value="120"/>
|
||||
<property name="severity" value="error"/>
|
||||
|
||||
<!--
|
||||
The default ignore pattern exempts the following elements:
|
||||
- import statements
|
||||
- long URLs inside comments
|
||||
-->
|
||||
|
||||
<property name="ignorePattern"
|
||||
value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.ignorePattern}"
|
||||
default="^(package .*;\s*)|(import .*;\s*)|( *\* *https?://.*)$"/>
|
||||
</module>
|
||||
|
||||
<module name="LeftCurly">
|
||||
<!-- Checks for placement of the left curly brace ('{'). -->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="RightCurly">
|
||||
<!-- Checks right curlies on CATCH, ELSE, and TRY blocks are on
|
||||
the same line. e.g., the following example is fine:
|
||||
<pre>
|
||||
if {
|
||||
...
|
||||
} else
|
||||
</pre>
|
||||
-->
|
||||
<!-- This next example is not fine:
|
||||
<pre>
|
||||
if {
|
||||
...
|
||||
}
|
||||
else
|
||||
</pre>
|
||||
-->
|
||||
<property name="option" value="same"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<!-- Checks for braces around if and else blocks -->
|
||||
<module name="NeedBraces">
|
||||
<property name="severity" value="warning"/>
|
||||
<property name="tokens" value="LITERAL_IF, LITERAL_ELSE, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO"/>
|
||||
</module>
|
||||
|
||||
<module name="UpperEll">
|
||||
<!-- Checks that long constants are defined with an upper ell.-->
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="FallThrough">
|
||||
<!-- Warn about falling through to the next case statement. Similar to
|
||||
javac -Xlint:fallthrough, but the check is suppressed if a single-line comment
|
||||
on the last non-blank line preceding the fallen-into case contains 'fall through' (or
|
||||
some other variants which we don't publicized to promote consistency).
|
||||
-->
|
||||
<property name="reliefPattern"
|
||||
value="fall through|Fall through|fallthru|Fallthru|falls through|Falls through|fallthrough|Fallthrough|No break|NO break|no break|continue on"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
MODIFIERS CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<module name="ModifierOrder">
|
||||
<!-- Warn if modifier order is inconsistent with JLS3 8.1.1, 8.3.1, and
|
||||
8.4.3. The prescribed order is:
|
||||
public, protected, private, abstract, static, final, transient, volatile,
|
||||
synchronized, native, strictfp
|
||||
-->
|
||||
</module>
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
WHITESPACE CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<module name="WhitespaceAround">
|
||||
<!-- Checks that various tokens are surrounded by whitespace.
|
||||
This includes most binary operators and keywords followed
|
||||
by regular or curly braces.
|
||||
-->
|
||||
<property name="tokens" value="ASSIGN, BAND, BAND_ASSIGN, BOR,
|
||||
BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN,
|
||||
EQUAL, GE, GT, LAND, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE,
|
||||
LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN,
|
||||
LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS,
|
||||
MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION,
|
||||
SL, SL_ASSIGN, SR_ASSIGN, STAR, STAR_ASSIGN"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="WhitespaceAfter">
|
||||
<!-- Checks that commas, semicolons and typecasts are followed by
|
||||
whitespace.
|
||||
-->
|
||||
<property name="tokens" value="COMMA, SEMI, TYPECAST"/>
|
||||
</module>
|
||||
|
||||
<module name="NoWhitespaceAfter">
|
||||
<!-- Checks that there is no whitespace after various unary operators.
|
||||
Linebreaks are allowed.
|
||||
-->
|
||||
<property name="tokens" value="BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS,
|
||||
UNARY_PLUS"/>
|
||||
<property name="allowLineBreaks" value="true"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="NoWhitespaceBefore">
|
||||
<!-- Checks that there is no whitespace before various unary operators.
|
||||
Linebreaks are allowed.
|
||||
-->
|
||||
<property name="tokens" value="SEMI, DOT, POST_DEC, POST_INC"/>
|
||||
<property name="allowLineBreaks" value="true"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="ParenPad">
|
||||
<!-- Checks that there is no whitespace before close parens or after
|
||||
open parens.
|
||||
-->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
</module>
|
||||
</module>
|
|
@ -1,13 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<FindBugsFilter>
|
||||
<Class name="com.microsoft.azure.spring.data.cosmosdb.common.MacAddress"/>
|
||||
<Bug pattern="NP_NONNULL_PARAM_VIOLATION"/>
|
||||
<Bug pattern="Unwritten field"/>
|
||||
<Bug pattern="SIC_INNER_SHOULD_BE_STATIC_ANON"/>
|
||||
|
||||
<Bug pattern="SIC_INNER_SHOULD_BE_STATIC_NEEDS_THIS"/>
|
||||
|
||||
<Bug pattern="UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD"/>
|
||||
|
||||
</FindBugsFilter>
|
35
install.sh
35
install.sh
|
@ -1,35 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
#---------------------------------------------------------------------------------------------
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
#---------------------------------------------------------------------------------------------
|
||||
|
||||
#
|
||||
# Bash script to install the Azure CLI
|
||||
#
|
||||
INSTALL_SCRIPT_URL="https://azurecliprod.blob.core.windows.net/install.py"
|
||||
INSTALL_SCRIPT_SHA256=7419f49b066015d863f398198c4ac5ad026f5aa3705e898b552e4e03fc352552
|
||||
install_script=az_cli_install
|
||||
|
||||
echo "Downloading Azure CLI install script from $INSTALL_SCRIPT_URL to $install_script."
|
||||
curl -# $INSTALL_SCRIPT_URL > $install_script || exit
|
||||
|
||||
if command -v sha256sum >/dev/null 2>&1
|
||||
then
|
||||
echo "$INSTALL_SCRIPT_SHA256 $install_script" | sha256sum -c - || exit
|
||||
elif command -v shasum >/dev/null 2>&1
|
||||
then
|
||||
echo "$INSTALL_SCRIPT_SHA256 $install_script" | shasum -a 256 -c - || exit
|
||||
fi
|
||||
|
||||
if ! command -v python >/dev/null 2>&1
|
||||
then
|
||||
echo "ERROR: Python not found. 'command -v python' returned failure."
|
||||
echo "If python is available on the system, add it to PATH. For example 'sudo ln -s /usr/bin/python3 /usr/bin/python'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
chmod 775 $install_script
|
||||
yes | /usr/bin/python3.5 ./$install_script > /dev/null
|
||||
sudo ln -sfv /home/travis/build/Microsoft/spring-data-cosmosdb/y/az /bin/az
|
|
@ -1,225 +0,0 @@
|
|||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Maven2 Start Up Batch script
|
||||
#
|
||||
# Required ENV vars:
|
||||
# ------------------
|
||||
# JAVA_HOME - location of a JDK home dir
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# M2_HOME - location of maven2's installed home dir
|
||||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
# e.g. to debug Maven itself, use
|
||||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||
|
||||
if [ -f /etc/mavenrc ] ; then
|
||||
. /etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.mavenrc" ] ; then
|
||||
. "$HOME/.mavenrc"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# OS specific support. $var _must_ be set to either true or false.
|
||||
cygwin=false;
|
||||
darwin=false;
|
||||
mingw=false
|
||||
case "`uname`" in
|
||||
CYGWIN*) cygwin=true ;;
|
||||
MINGW*) mingw=true;;
|
||||
Darwin*) darwin=true
|
||||
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
if [ -x "/usr/libexec/java_home" ]; then
|
||||
export JAVA_HOME="`/usr/libexec/java_home`"
|
||||
else
|
||||
export JAVA_HOME="/Library/Java/Home"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
if [ -r /etc/gentoo-release ] ; then
|
||||
JAVA_HOME=`java-config --jre-home`
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$M2_HOME" ] ; then
|
||||
## resolve links - $0 may be a link to maven's home
|
||||
PRG="$0"
|
||||
|
||||
# need this for relative symlinks
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG="`dirname "$PRG"`/$link"
|
||||
fi
|
||||
done
|
||||
|
||||
saveddir=`pwd`
|
||||
|
||||
M2_HOME=`dirname "$PRG"`/..
|
||||
|
||||
# make it fully qualified
|
||||
M2_HOME=`cd "$M2_HOME" && pwd`
|
||||
|
||||
cd "$saveddir"
|
||||
# echo Using m2 at $M2_HOME
|
||||
fi
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||
if $cygwin ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --unix "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
|
||||
fi
|
||||
|
||||
# For Migwn, ensure paths are in UNIX format before anything is touched
|
||||
if $mingw ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME="`(cd "$M2_HOME"; pwd)`"
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
|
||||
# TODO classpath?
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
javaExecutable="`which javac`"
|
||||
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
|
||||
# readlink(1) is not available as standard on Solaris 10.
|
||||
readLink=`which readlink`
|
||||
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
|
||||
if $darwin ; then
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
|
||||
else
|
||||
javaExecutable="`readlink -f \"$javaExecutable\"`"
|
||||
fi
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
|
||||
JAVA_HOME="$javaHome"
|
||||
export JAVA_HOME
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$JAVACMD" ] ; then
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
else
|
||||
JAVACMD="`which java`"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||
echo " We cannot execute $JAVACMD" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
echo "Warning: JAVA_HOME environment variable is not set."
|
||||
fi
|
||||
|
||||
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
|
||||
|
||||
# traverses directory structure from process work directory to filesystem root
|
||||
# first directory with .mvn subdirectory is considered project base directory
|
||||
find_maven_basedir() {
|
||||
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
echo "Path not specified to find_maven_basedir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
basedir="$1"
|
||||
wdir="$1"
|
||||
while [ "$wdir" != '/' ] ; do
|
||||
if [ -d "$wdir"/.mvn ] ; then
|
||||
basedir=$wdir
|
||||
break
|
||||
fi
|
||||
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||
if [ -d "${wdir}" ]; then
|
||||
wdir=`cd "$wdir/.."; pwd`
|
||||
fi
|
||||
# end of workaround
|
||||
done
|
||||
echo "${basedir}"
|
||||
}
|
||||
|
||||
# concatenates all lines of a file
|
||||
concat_lines() {
|
||||
if [ -f "$1" ]; then
|
||||
echo "$(tr -s '\n' ' ' < "$1")"
|
||||
fi
|
||||
}
|
||||
|
||||
BASE_DIR=`find_maven_basedir "$(pwd)"`
|
||||
if [ -z "$BASE_DIR" ]; then
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
|
||||
echo $MAVEN_PROJECTBASEDIR
|
||||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --path --windows "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
|
||||
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
||||
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
|
||||
fi
|
||||
|
||||
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
exec "$JAVACMD" \
|
||||
$MAVEN_OPTS \
|
||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
|
@ -1,143 +0,0 @@
|
|||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Maven2 Start Up Batch script
|
||||
@REM
|
||||
@REM Required ENV vars:
|
||||
@REM JAVA_HOME - location of a JDK home dir
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM M2_HOME - location of maven2's installed home dir
|
||||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
|
||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
@REM e.g. to debug Maven itself, use
|
||||
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||
@echo off
|
||||
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
|
||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||
|
||||
@REM set %HOME% to equivalent of $HOME
|
||||
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||
|
||||
@REM Execute a user defined script before this one
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
|
||||
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
|
||||
:skipRcPre
|
||||
|
||||
@setlocal
|
||||
|
||||
set ERROR_CODE=0
|
||||
|
||||
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||
@setlocal
|
||||
|
||||
@REM ==== START VALIDATION ====
|
||||
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME not found in your environment. >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
:OkJHome
|
||||
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
@REM ==== END VALIDATION ====
|
||||
|
||||
:init
|
||||
|
||||
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||
@REM Fallback to current working directory if not found.
|
||||
|
||||
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||
|
||||
set EXEC_DIR=%CD%
|
||||
set WDIR=%EXEC_DIR%
|
||||
:findBaseDir
|
||||
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||
cd ..
|
||||
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||
set WDIR=%CD%
|
||||
goto findBaseDir
|
||||
|
||||
:baseDirFound
|
||||
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||
cd "%EXEC_DIR%"
|
||||
goto endDetectBaseDir
|
||||
|
||||
:baseDirNotFound
|
||||
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||
cd "%EXEC_DIR%"
|
||||
|
||||
:endDetectBaseDir
|
||||
|
||||
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||
|
||||
@setlocal EnableExtensions EnableDelayedExpansion
|
||||
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||
|
||||
:endReadAdditionalConfig
|
||||
|
||||
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||
|
||||
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
|
||||
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
||||
if ERRORLEVEL 1 goto error
|
||||
goto end
|
||||
|
||||
:error
|
||||
set ERROR_CODE=1
|
||||
|
||||
:end
|
||||
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
|
||||
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
|
||||
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
|
||||
:skipRcPost
|
||||
|
||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||
if "%MAVEN_BATCH_PAUSE%" == "on" pause
|
||||
|
||||
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
|
||||
|
||||
exit /B %ERROR_CODE%
|
519
pom.xml
519
pom.xml
|
@ -1,519 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.microsoft.azure</groupId>
|
||||
<artifactId>spring-data-cosmosdb</artifactId>
|
||||
<version>2.3.1-SNAPSHOT</version>
|
||||
|
||||
<name>Spring Data for Azure Cosmos DB SQL API</name>
|
||||
<description>Spring Data for Azure Cosmos DB SQL API</description>
|
||||
<url>https://github.com/Microsoft/spring-data-cosmosdb</url>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>MIT</name>
|
||||
<url>https://github.com/Microsoft/spring-data-cosmosdb/blob/master/LICENSE</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<id>yungez</id>
|
||||
<name>Yunge Zhu</name>
|
||||
<email>yungez@microsoft.com</email>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<organization>
|
||||
<name>Microsoft</name>
|
||||
<url>https://www.microsoft.com</url>
|
||||
</organization>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/Microsoft/spring-data-cosmosdb.git</connection>
|
||||
<developerConnection>scm:git:ssh://github.com:Microsoft/spring-data-cosmosdb.git
|
||||
</developerConnection>
|
||||
<url>https://github.com/Microsoft/spring-data-cosmosdb/tree/master</url>
|
||||
</scm>
|
||||
<properties>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.build.timestamp.format>MM-dd-HH-mm-ss</maven.build.timestamp.format>
|
||||
|
||||
<spring.springframework.version>5.2.6.RELEASE</spring.springframework.version>
|
||||
<spring.data.version>2.3.0.RELEASE</spring.data.version>
|
||||
<fasterxml.jackson.version>2.10.0</fasterxml.jackson.version>
|
||||
|
||||
<mockito.core.version>2.8.9</mockito.core.version>
|
||||
<powermock.version>1.7.1</powermock.version>
|
||||
<spring.boot.starter.test.version>2.3.0.RELEASE</spring.boot.starter.test.version>
|
||||
<org.projectlombok.version>1.16.18</org.projectlombok.version>
|
||||
<java.tuples.version>1.2</java.tuples.version>
|
||||
<slf4j.version>1.7.25</slf4j.version>
|
||||
<gson.version>2.8.4</gson.version>
|
||||
<project.reactor.test.version>3.3.0.RELEASE</project.reactor.test.version>
|
||||
|
||||
<azure.cosmos.version>3.7.3</azure.cosmos.version>
|
||||
<azure.test.resourcegroup>spring-data-cosmosdb-test</azure.test.resourcegroup>
|
||||
<azure.test.dbname>testdb-${maven.build.timestamp}</azure.test.dbname>
|
||||
<skip.integration.tests>true</skip.integration.tests>
|
||||
<test.on.azure>false</test.on.azure>
|
||||
<test.on.emualator>false</test.on.emualator>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<version>${spring.springframework.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>${spring.springframework.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
<version>${spring.springframework.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<version>${spring.springframework.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
<version>${spring.springframework.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-commons</artifactId>
|
||||
<version>${spring.data.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-expression</artifactId>
|
||||
<version>${spring.springframework.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.azure</groupId>
|
||||
<artifactId>azure-cosmos</artifactId>
|
||||
<version>${azure.cosmos.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.module</groupId>
|
||||
<artifactId>jackson-module-parameter-names</artifactId>
|
||||
<version>${fasterxml.jackson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jdk8</artifactId>
|
||||
<version>${fasterxml.jackson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
<version>${fasterxml.jackson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20140107</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${org.projectlombok.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.javatuples</groupId>
|
||||
<artifactId>javatuples</artifactId>
|
||||
<version>${java.tuples.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>${mockito.core.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-module-junit4</artifactId>
|
||||
<version>${powermock.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-api-mockito2</artifactId>
|
||||
<version>${powermock.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<version>${spring.boot.starter.test.version}</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.vaadin.external.google</groupId>
|
||||
<artifactId>android-json</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<version>${project.reactor.test.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>${gson.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
<includes>
|
||||
<include>META-INF/project.properties</include>
|
||||
<include>telemetry.config</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.10.4</version>
|
||||
<configuration>
|
||||
<show>private</show>
|
||||
<failOnError>false</failOnError>
|
||||
<sourceFileExcludes>
|
||||
<exclude>
|
||||
com/microsoft/azure/spring/data/cosmosdb/documentdb/core/mapping/BasicCosmosPersistentProperty.java
|
||||
</exclude>
|
||||
</sourceFileExcludes>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<additionalparam>${javadoc.opts}</additionalparam>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>2.17</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>validate</id>
|
||||
<phase>validate</phase>
|
||||
<configuration>
|
||||
<configLocation>${project.basedir}/config/checkstyle.xml</configLocation>
|
||||
<encoding>UTF-8</encoding>
|
||||
<consoleOutput>true</consoleOutput>
|
||||
<failsOnError>true</failsOnError>
|
||||
<failOnViolation>true</failOnViolation>
|
||||
<includeTestSourceDirectory>true</includeTestSourceDirectory>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<linkXRef>false</linkXRef>
|
||||
</configuration>
|
||||
<inherited>true</inherited>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-help-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>show-profiles</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>active-profiles</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<version>3.0.5</version>
|
||||
<configuration>
|
||||
<effort>Max</effort>
|
||||
<threshold>Low</threshold>
|
||||
<xmlOutput>true</xmlOutput>
|
||||
<findbugsXmlOutputDirectory>${project.build.directory}/findbugs
|
||||
</findbugsXmlOutputDirectory>
|
||||
<excludeFilterFile>${project.basedir}/config/findbugs-exclude.xml</excludeFilterFile>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.ant</groupId>
|
||||
<artifactId>ant</artifactId>
|
||||
<version>1.9.4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<version>2.7</version>
|
||||
<configuration>
|
||||
<formats>
|
||||
<format>html</format>
|
||||
<format>xml</format>
|
||||
</formats>
|
||||
<check>
|
||||
<haltOnFailure>true</haltOnFailure>
|
||||
<branchRate>65</branchRate>
|
||||
<totalBranchRate>65</totalBranchRate>
|
||||
</check>
|
||||
<instrumentation>
|
||||
<excludes>
|
||||
<exclude>com/microsoft/azure/**/GetHashMac.class</exclude>
|
||||
<exclude>com/microsoft/azure/**/Constants.class</exclude>
|
||||
</excludes>
|
||||
</instrumentation>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>2.19</version>
|
||||
<configuration>
|
||||
<systemPropertiesFile>src/test/resources/application.properties</systemPropertiesFile>
|
||||
<skipITs>${skip.integration.tests}</skipITs>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>integration-test</id>
|
||||
<goals>
|
||||
<goal>integration-test</goal>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<version>1.8</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>setup</id>
|
||||
<phase>pre-integration-test</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<target if="${test.on.azure}">
|
||||
<ant antfile="build.xml" target="setup">
|
||||
</ant>
|
||||
</target>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
<execution>
|
||||
<id>cleanup</id>
|
||||
<phase>post-integration-test</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<target if="${test.on.azure}">
|
||||
<ant antfile="build.xml" target="cleanup"/>
|
||||
</target>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>dev</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<properties>
|
||||
<build.profile.id>dev</build.profile.id>
|
||||
<skip.integration.tests>true</skip.integration.tests>
|
||||
<test.on.azure>false</test.on.azure>
|
||||
<test.on.emulator>false</test.on.emulator>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>integration-test-azure</id>
|
||||
<properties>
|
||||
<build.profile.id>integration-test-azure</build.profile.id>
|
||||
<skip.integration.tests>false</skip.integration.tests>
|
||||
<test.on.azure>true</test.on.azure>
|
||||
<test.on.emulator>false</test.on.emulator>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>travis-ci-test</id>
|
||||
<properties>
|
||||
<build.profile.id>travis-ci-test</build.profile.id>
|
||||
<skip.integration.tests>false</skip.integration.tests>
|
||||
<test.on.azure>true</test.on.azure>
|
||||
<test.on.emulator>false</test.on.emulator>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>performance-test</id>
|
||||
<properties>
|
||||
<build.profile.id>performance-test</build.profile.id>
|
||||
<skip.integration.tests>false</skip.integration.tests>
|
||||
<test.on.azure>false</test.on.azure>
|
||||
<test.on.emulator>false</test.on.emulator>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>PerformanceCompare.java</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>integration-test-emulator</id>
|
||||
<properties>
|
||||
<build.profile.id>integration-test-emulator</build.profile.id>
|
||||
<skip.integration.tests>false</skip.integration.tests>
|
||||
<test.on.azure>false</test.on.azure>
|
||||
<test.on.emulator>true</test.on.emulator>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<!--Excluding PageablePersonRepositoryIT test class, as it creates large documents which are not suitable for emulator-->
|
||||
<exclude>**/PageablePersonRepositoryIT.java</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>release</name>
|
||||
</property>
|
||||
</activation>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>doclint-java8-disable</id>
|
||||
<activation>
|
||||
<jdk>[1.8,)</jdk>
|
||||
</activation>
|
||||
<properties>
|
||||
<javadoc.opts>-Xdoclint:none</javadoc.opts>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
|
@ -1,22 +0,0 @@
|
|||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2018 Pan Li
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,75 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.microsoft.azure</groupId>
|
||||
<artifactId>spring-data-cosmosdb-samples</artifactId>
|
||||
<version>2.3.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>spring-data-cosmosdb-example</artifactId>
|
||||
<name>Spring Data Cosmosdb - Example</name>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.azure</groupId>
|
||||
<artifactId>spring-data-cosmosdb</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-rest</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-rest-webmvc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<!-- Build an executable JAR -->
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>example.springdata.cosmosdb.Application</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* Copyright 2015-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cosmosdb;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.springframework.data.annotation.Id;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Address {
|
||||
|
||||
@Id
|
||||
private String postalCode;
|
||||
|
||||
private String street;
|
||||
|
||||
private String city;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s, %s, %s", this.street, this.city, this.postalCode);
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
/*
|
||||
* Copyright 2015-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cosmosdb;
|
||||
|
||||
import org.assertj.core.util.Lists;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.data.domain.Sort;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@SpringBootApplication
|
||||
public class Application implements CommandLineRunner{
|
||||
|
||||
private static final String ID_1 = "id_1";
|
||||
private static final String NAME_1 = "myName_1";
|
||||
|
||||
private static final String ID_2 = "id_2";
|
||||
private static final String NAME_2 = "myName_2";
|
||||
|
||||
private static final String ID_3 = "id_3";
|
||||
private static final String NAME_3 = "myName_3";
|
||||
|
||||
private static final String EMAIL = "xxx-xx@xxx.com";
|
||||
private static final String POSTAL_CODE = "0123456789";
|
||||
private static final String STREET = "zixing road";
|
||||
private static final String CITY = "shanghai";
|
||||
private static final String ROLE_CREATOR = "creator";
|
||||
private static final String ROLE_CONTRIBUTOR = "contributor";
|
||||
private static final int COST_CREATOR = 234;
|
||||
private static final int COST_CONTRIBUTOR = 666;
|
||||
private static final Long COUNT = 123L;
|
||||
|
||||
private final Address address = new Address(POSTAL_CODE, STREET, CITY);
|
||||
private final Role creator = new Role(ROLE_CREATOR, COST_CREATOR);
|
||||
private final Role contributor = new Role(ROLE_CONTRIBUTOR, COST_CONTRIBUTOR);
|
||||
private final User user_1 = new User(ID_1, EMAIL, NAME_1, COUNT, address, Arrays.asList(creator, contributor));
|
||||
private final User user_2 = new User(ID_2, EMAIL, NAME_2, COUNT, address, Arrays.asList(creator, contributor));
|
||||
private final User user_3 = new User(ID_3, EMAIL, NAME_3, COUNT, address, Arrays.asList(creator, contributor));
|
||||
|
||||
@Autowired
|
||||
private UserRepository repository;
|
||||
|
||||
public static void main(String... args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
printList(this.repository.findByEmailOrName(this.user_1.getEmail(),
|
||||
this.user_1.getName()).collectList().block());
|
||||
|
||||
printList(this.repository.findByCount(COUNT,
|
||||
Sort.by(new Sort.Order(Sort.Direction.ASC, "count"))).collectList().block());
|
||||
|
||||
printList(this.repository.findByNameIn(Arrays.asList(this.user_1.getName(),
|
||||
"fake-name")).collectList().block());
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void setup() {
|
||||
this.repository.save(user_1).block();
|
||||
this.repository.saveAll(Lists.newArrayList(user_2, user_3)).collectList().block();
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void cleanup() {
|
||||
this.repository.deleteAll().block();
|
||||
}
|
||||
|
||||
private void printList(List<User> users) {
|
||||
users.forEach(System.out::println);
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cosmosdb;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ConfigurationProperties(prefix = "azure.cosmosdb")
|
||||
public class CosmosDbProperties {
|
||||
|
||||
private String uri;
|
||||
|
||||
private String key;
|
||||
|
||||
private String secondaryKey;
|
||||
|
||||
private String database;
|
||||
|
||||
private boolean populateQueryMetrics;
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Copyright 2015-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cosmosdb;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Setter
|
||||
@Getter
|
||||
public class Role {
|
||||
|
||||
private String name;
|
||||
|
||||
int cost;
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* Copyright 2015-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package example.springdata.cosmosdb;
|
||||
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.mapping.Document;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.mapping.PartitionKey;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.springframework.data.annotation.Id;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Document(collection = "mycollection")
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public class User {
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
private String email;
|
||||
|
||||
@PartitionKey
|
||||
private String name;
|
||||
|
||||
private Long count;
|
||||
|
||||
private Address address;
|
||||
|
||||
private List<Role> roleList;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s: %s %s %s", this.id, this.email, this.name, this.address.toString());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Copyright 2015-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package example.springdata.cosmosdb;
|
||||
|
||||
import com.microsoft.azure.spring.data.cosmosdb.repository.ReactiveCosmosRepository;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@Repository
|
||||
@RepositoryRestResource(collectionResourceRel = "user", path = "user")
|
||||
public interface UserRepository extends ReactiveCosmosRepository<User, String> {
|
||||
|
||||
Flux<User> findByName(String firstName);
|
||||
|
||||
Flux<User> findByEmailAndAddress(String email, Address address);
|
||||
|
||||
Flux<User> findByEmailOrName(String email, String name);
|
||||
|
||||
Flux<User> findByCount(Long count, Sort sort);
|
||||
|
||||
Flux<User> findByNameIn(Collection<String> names);
|
||||
|
||||
Flux<User> findByAddress(Address address);
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cosmosdb;
|
||||
|
||||
import com.azure.data.cosmos.CosmosKeyCredential;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.config.AbstractCosmosConfiguration;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.config.CosmosDBConfig;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.ResponseDiagnostics;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.ResponseDiagnosticsProcessor;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.repository.config.EnableReactiveCosmosRepositories;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(CosmosDbProperties.class)
|
||||
@EnableReactiveCosmosRepositories
|
||||
@PropertySource("classpath:application.properties")
|
||||
@Slf4j
|
||||
public class UserRepositoryConfiguration extends AbstractCosmosConfiguration {
|
||||
|
||||
@Autowired
|
||||
private CosmosDbProperties properties;
|
||||
|
||||
private CosmosKeyCredential cosmosKeyCredential;
|
||||
|
||||
@Bean
|
||||
public CosmosDBConfig cosmosDbConfig() {
|
||||
this.cosmosKeyCredential = new CosmosKeyCredential(properties.getKey());
|
||||
CosmosDBConfig cosmosDBConfig = CosmosDBConfig.builder(properties.getUri(), cosmosKeyCredential,
|
||||
properties.getDatabase()).build();
|
||||
cosmosDBConfig.setPopulateQueryMetrics(properties.isPopulateQueryMetrics());
|
||||
cosmosDBConfig.setResponseDiagnosticsProcessor(new ResponseDiagnosticsProcessorImplementation());
|
||||
return cosmosDBConfig;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private static class ResponseDiagnosticsProcessorImplementation implements ResponseDiagnosticsProcessor {
|
||||
|
||||
@Override
|
||||
public void processResponseDiagnostics(@Nullable ResponseDiagnostics responseDiagnostics) {
|
||||
log.info("Response Diagnostics {}", responseDiagnostics);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=example.springdata.cosmosdb.UserRepositoryConfiguration
|
|
@ -1,5 +0,0 @@
|
|||
azure.cosmosdb.uri=your-cosmosDb-uri
|
||||
azure.cosmosdb.key=your-cosmosDb-key
|
||||
azure.cosmosdb.secondaryKey=your-cosmosDb-secondary-key
|
||||
azure.cosmosdb.database=your-cosmosDb-dbName
|
||||
azure.cosmosdb.populateQueryMetrics=if-populate-query-metrics
|
|
@ -1,178 +0,0 @@
|
|||
/*
|
||||
* Copyright 2015-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cosmosdb;
|
||||
|
||||
import com.microsoft.azure.spring.data.cosmosdb.exception.CosmosDBAccessException;
|
||||
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.Sort;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.Assert;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes = {UserRepositoryConfiguration.class})
|
||||
public class UserRepositoryIntegrationTest {
|
||||
|
||||
private static final String ID = "0123456789";
|
||||
private static final String EMAIL = "xxx-xx@xxx.com";
|
||||
private static final String NAME = "myName";
|
||||
private static final String POSTAL_CODE = "0123456789";
|
||||
private static final String STREET = "zixing road";
|
||||
private static final String CITY = "shanghai";
|
||||
private static final String ROLE_CREATOR = "creator";
|
||||
private static final String ROLE_CONTRIBUTOR = "contributor";
|
||||
private static final int COST_CREATOR = 234;
|
||||
private static final int COST_CONTRIBUTOR = 666;
|
||||
private static final Long COUNT = 123L;
|
||||
|
||||
@Autowired
|
||||
private UserRepository repository;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@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().block();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserRepository() {
|
||||
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).block();
|
||||
|
||||
// Test for findById
|
||||
User result = this.repository.findById(ID).block();
|
||||
Assert.notNull(result, "should be exist in database");
|
||||
Assert.isTrue(result.getId().equals(ID), "should be the same id");
|
||||
|
||||
// Test for findByName
|
||||
List<User> resultList = this.repository.findByName(user.getName()).collectList().block();
|
||||
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");
|
||||
|
||||
for (int i = 0; i < user.getRoleList().size(); i++) {
|
||||
final Role role = result.getRoleList().get(i);
|
||||
final Role roleReference = user.getRoleList().get(i);
|
||||
|
||||
Assert.isTrue(role.getName().equals(roleReference.getName()), "should be the same role name");
|
||||
Assert.isTrue(role.getCost() == roleReference.getCost(), "should be the same role cost");
|
||||
}
|
||||
|
||||
// Test for findByEmailAndAddress
|
||||
resultList = this.repository.findByEmailAndAddress(user.getEmail(), user.getAddress()).collectList().block();
|
||||
Assert.isTrue(resultList.size() == 1, "should be only one user here");
|
||||
|
||||
result = resultList.get(0);
|
||||
Assert.isTrue(result.getEmail().equals(user.getEmail()), "should be same Email");
|
||||
Assert.isTrue(result.getAddress().getPostalCode().equals(user.getAddress().getPostalCode()),
|
||||
"should be same postalCode");
|
||||
Assert.isTrue(result.getAddress().getCity().equals(user.getAddress().getCity()), "should be same City");
|
||||
Assert.isTrue(result.getAddress().getStreet().equals(user.getAddress().getStreet()), "should be same street");
|
||||
|
||||
resultList = this.repository.findByEmailOrName(user.getEmail(), user.getName()).collectList().block();
|
||||
result = resultList.get(0);
|
||||
Assert.isTrue(result.getId().equals(user.getId()), "should be the same Id");
|
||||
|
||||
resultList = this.repository.findByCount(COUNT,
|
||||
Sort.by(new Sort.Order(Sort.Direction.ASC, "count"))).collectList().block();
|
||||
result = resultList.get(0);
|
||||
Assert.isTrue(result.getId().equals(user.getId()), "should be the same Id");
|
||||
|
||||
resultList = this.repository.findByNameIn(Arrays.asList(user.getName(), "fake-name")).collectList().block();
|
||||
result = resultList.get(0);
|
||||
Assert.isTrue(result.getId().equals(user.getId()), "should be the same Id");
|
||||
|
||||
// Test for findByAddress
|
||||
final Flux<User> findByAddressFlux = this.repository.findByAddress(address);
|
||||
resultList = findByAddressFlux.collectList().block();
|
||||
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).block();
|
||||
|
||||
// Test for findById
|
||||
final User result = this.repository.findById(ID).block();
|
||||
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()).collectList().block();
|
||||
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 = CosmosDBAccessException.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).block();
|
||||
|
||||
// Test for findById
|
||||
final User result = this.repository.findById(ID).block();
|
||||
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.findById(user.getId()).block();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
azure.cosmosdb.uri=your-cosmosDb-uri
|
||||
azure.cosmosdb.key=your-cosmosDb-key
|
||||
azure.cosmosdb.secondaryKey=your-cosmosDb-secondary-key
|
||||
azure.cosmosdb.database=your-cosmosDb-dbName
|
||||
azure.cosmosdb.populateQueryMetrics=if-populate-query-metrics
|
|
@ -1,42 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.microsoft.azure</groupId>
|
||||
<artifactId>spring-data-cosmosdb-samples</artifactId>
|
||||
<version>2.3.0</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.0.RELEASE</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<name>Spring Data CosmosDB - Samples</name>
|
||||
<description>Sample projects for Spring Data CosmosDB</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<modules>
|
||||
<module>example</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
|
@ -1,31 +0,0 @@
|
|||
@echo off
|
||||
rem cleanup test resource
|
||||
|
||||
set clientId=%CLIENT_ID%
|
||||
set clientKey=%CLIENT_KEY%
|
||||
set tenantId=%TENANT_ID%
|
||||
|
||||
if "%clientId%" == "" (
|
||||
goto :end
|
||||
)
|
||||
if "%clientKey%" == "" (
|
||||
goto :end
|
||||
)
|
||||
if "%tenantId%" == "" (
|
||||
goto :end
|
||||
)
|
||||
|
||||
set resourcegroup=%1
|
||||
set dbname=%2
|
||||
|
||||
if "%resourcegroup" == "" (
|
||||
goto :end
|
||||
)
|
||||
if "%dbname" == "" (
|
||||
goto :end
|
||||
)
|
||||
|
||||
call az login --service-principal -u %clientId% -p %clientKey% --tenant %tenantId% >> tmp.txt
|
||||
call az cosmosdb delete --name %dbname% --resource-group %resourcegroup%
|
||||
|
||||
:end
|
|
@ -1,17 +0,0 @@
|
|||
# cleanup test resources
|
||||
#!/bin/bash
|
||||
resourcegroup=$1
|
||||
dbname=$2
|
||||
|
||||
if [ "$CLIENT_ID" == "" ]; then
|
||||
exit 0
|
||||
fi
|
||||
if [ "$CLIENT_KEY" == "" ]; then
|
||||
exit 0
|
||||
fi
|
||||
if [ "$TENANT_ID" == "" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
az login --service-principal -u $CLIENT_ID -p $CLIENT_KEY --tenant $TENANT_ID >> tmp.txt
|
||||
az cosmosdb delete --name $dbname --resource-group $resourcegroup
|
|
@ -1,24 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
endpoint_pr="$COSMOSDB_ENDPOINT_PR"
|
||||
password_pr="$COSMOSDB_PASSWORD_PR"
|
||||
|
||||
endpoint_push="$COSMOSDB_ENDPOINT_PUSH"
|
||||
password_push="$COSMOSDB_PASSWORD_PUSH"
|
||||
|
||||
git_branch=$(git branch | grep "*" | cut -d ' ' -f2-)
|
||||
|
||||
if [[ "$git_branch" = *"FETCH_HEAD"* ]]; then
|
||||
endpoint=$endpoint_pr
|
||||
password=$password_pr
|
||||
else
|
||||
endpoint=$endpoint_push
|
||||
password=$password_push
|
||||
fi
|
||||
|
||||
cat << EOF
|
||||
cosmosdb.key=$password
|
||||
cosmosdb.uri=$endpoint
|
||||
|
||||
EOF
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
@echo off
|
||||
set clientId=%CLIENT_ID%
|
||||
set clientKey=%CLIENT_KEY%
|
||||
set tenantId=%TENANT_ID%
|
||||
|
||||
if "%clientId%" == "" (
|
||||
goto :noSetup
|
||||
)
|
||||
if "%clientKey%" == "" (
|
||||
goto :noSetup
|
||||
)
|
||||
if "%tenantId%" == "" (
|
||||
goto :noSetup
|
||||
)
|
||||
|
||||
set resourcegroup=%1
|
||||
set dbname=%2
|
||||
|
||||
if "%resourcegroup" == "" (
|
||||
goto :noSetup
|
||||
)
|
||||
if "%dbname" == "" (
|
||||
goto :noSetup
|
||||
)
|
||||
|
||||
call az login --service-principal -u %clientId% -p %clientKey% --tenant %tenantId% >> null
|
||||
call az group create -l westus -n %resourcegroup% >> null
|
||||
set createcmd='az cosmosdb create --name %dbname% --resource-group %resourcegroup% --kind GlobalDocumentDB --query documentEndpoint'
|
||||
|
||||
for /f "tokens=*" %%a in (%createcmd%) do (set cosmosdburi=%%a)
|
||||
|
||||
set listcmd='az cosmosdb keys list --name %dbname% --resource-group %resourcegroup% --query primaryMasterKey'
|
||||
for /f "tokens=*" %%a in (%listcmd%) do (set cosmosdbPrimarykey=%%a)
|
||||
set cosmosdbPrimarykey=%cosmosdbPrimarykey:"=%
|
||||
|
||||
set listSecondaryKeycmd='az cosmosdb keys list --name %dbname% --resource-group %resourcegroup% --query secondaryMasterKey'
|
||||
for /f "tokens=*" %%a in (%listSecondaryKeycmd%) do (set cosmosdbSecondarykey=%%a)
|
||||
set cosmosdbSecondarykey=%cosmosdbSecondarykey:"=%
|
||||
|
||||
echo %cosmosdbPrimarykey% %cosmosdbSecondarykey%
|
||||
|
||||
goto :end
|
||||
|
||||
:noSetup
|
||||
echo not to setup test resources
|
||||
exit 0
|
||||
|
||||
:end
|
|
@ -1,22 +0,0 @@
|
|||
# setup test resources
|
||||
#!/bin/bash
|
||||
resourcegroup=$1
|
||||
dbname=$2
|
||||
|
||||
if [ -z "$CLIENT_ID" ]; then
|
||||
exit 0
|
||||
fi
|
||||
if [ -z "$CLIENT_KEY" ]; then
|
||||
exit 0
|
||||
fi
|
||||
if [ -z "$TENANT_ID" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
az login --service-principal -u $CLIENT_ID -p $CLIENT_KEY --tenant $TENANT_ID >> tmp.txt
|
||||
az group create -l westus -n $resourcegroup 2>1 > /dev/null
|
||||
cosmosDbUri=$(az cosmosdb create --name $dbname --resource-group $resourcegroup --kind GlobalDocumentDB --query documentEndpoint)
|
||||
cosmosDbPrimaryKey=$(az cosmosdb keys list --name $dbname --resource-group $resourcegroup --query primaryMasterKey)
|
||||
cosmosDbSecondaryKey=$(az cosmosdb keys list --name $dbname --resource-group $resourcegroup --query secondaryMasterKey)
|
||||
|
||||
echo "$cosmosDbPrimaryKey $cosmosDbSecondaryKey" | sed -e 's/"//g'
|
|
@ -1,9 +0,0 @@
|
|||
function write_visual_bells() {
|
||||
while true; do
|
||||
echo -en "\a"
|
||||
sleep 10
|
||||
done
|
||||
}
|
||||
write_visual_bells&
|
||||
|
||||
set -o pipefail && mvn -P travis-ci-test clean cobertura:cobertura-integration-test | grep -v "DEBUG"
|
|
@ -1,35 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb;
|
||||
|
||||
import com.azure.data.cosmos.IndexingMode;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class Constants {
|
||||
|
||||
public static final String DEFAULT_COLLECTION_NAME = "";
|
||||
public static final String DEFAULT_REQUEST_UNIT = "4000";
|
||||
public static final boolean DEFAULT_INDEXINGPOLICY_AUTOMATIC = true;
|
||||
public static final IndexingMode DEFAULT_INDEXINGPOLICY_MODE = IndexingMode.CONSISTENT;
|
||||
public static final String DEFAULT_REPOSITORY_IMPLEMENT_POSTFIX = "Impl";
|
||||
public static final int DEFAULT_TIME_TO_LIVE = -1; // Indicates never expire
|
||||
public static final boolean DEFAULT_AUTO_CREATE_CONTAINER = true;
|
||||
|
||||
public static final String ID_PROPERTY_NAME = "id";
|
||||
|
||||
public static final String COSMOSDB_MODULE_NAME = "cosmosdb";
|
||||
public static final String COSMOSDB_MODULE_PREFIX = "cosmosdb";
|
||||
public static final String COSMOS_MAPPING_CONTEXT = "cosmosMappingContext";
|
||||
|
||||
public static final String USER_AGENT_SUFFIX = "spring-data/";
|
||||
|
||||
public static final String OBJECTMAPPER_BEAN_NAME = "cosmosdbObjectMapper";
|
||||
|
||||
public static final String ISO_8601_COMPATIBLE_DATE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss:SSSXXX";
|
||||
}
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb;
|
||||
|
||||
import com.azure.data.cosmos.ConnectionPolicy;
|
||||
import com.azure.data.cosmos.CosmosClient;
|
||||
import com.azure.data.cosmos.sync.CosmosSyncClient;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.common.MacAddress;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.common.PropertyLoader;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.common.TelemetrySender;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.config.CosmosDBConfig;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
public class CosmosDbFactory {
|
||||
|
||||
@Getter
|
||||
private final CosmosDBConfig config;
|
||||
|
||||
private static final boolean IS_TELEMETRY_ALLOWED = PropertyLoader.isApplicationTelemetryAllowed();
|
||||
|
||||
private static final String USER_AGENT_SUFFIX = Constants.USER_AGENT_SUFFIX + PropertyLoader.getProjectVersion();
|
||||
|
||||
private String getUserAgentSuffix() {
|
||||
String suffix = ";" + USER_AGENT_SUFFIX;
|
||||
|
||||
if (IS_TELEMETRY_ALLOWED || config.isAllowTelemetry()) {
|
||||
suffix += ";" + MacAddress.getHashMac();
|
||||
}
|
||||
|
||||
return suffix;
|
||||
}
|
||||
|
||||
public CosmosDbFactory(@NonNull CosmosDBConfig config) {
|
||||
validateConfig(config);
|
||||
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public CosmosClient getCosmosClient() {
|
||||
final ConnectionPolicy policy = config.getConnectionPolicy();
|
||||
final String userAgent = getUserAgentSuffix() + ";" + policy.userAgentSuffix();
|
||||
|
||||
policy.userAgentSuffix(userAgent);
|
||||
return CosmosClient.builder()
|
||||
.endpoint(config.getUri())
|
||||
.key(config.getKey())
|
||||
.cosmosKeyCredential(config.getCosmosKeyCredential())
|
||||
.connectionPolicy(policy)
|
||||
.consistencyLevel(config.getConsistencyLevel())
|
||||
.build();
|
||||
}
|
||||
|
||||
public CosmosSyncClient getCosmosSyncClient() {
|
||||
final ConnectionPolicy policy = config.getConnectionPolicy();
|
||||
final String userAgent = getUserAgentSuffix() + ";" + policy.userAgentSuffix();
|
||||
|
||||
policy.userAgentSuffix(userAgent);
|
||||
return CosmosClient.builder()
|
||||
.endpoint(config.getUri())
|
||||
.key(config.getKey())
|
||||
.cosmosKeyCredential(config.getCosmosKeyCredential())
|
||||
.connectionPolicy(policy)
|
||||
.consistencyLevel(config.getConsistencyLevel())
|
||||
.buildSyncClient();
|
||||
}
|
||||
|
||||
private void validateConfig(@NonNull CosmosDBConfig config) {
|
||||
Assert.hasText(config.getUri(), "cosmosdb host url 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!");
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
private void sendTelemetry() {
|
||||
// If any one of them is enabled, send telemetry data
|
||||
if (IS_TELEMETRY_ALLOWED || config.isAllowTelemetry()) {
|
||||
final TelemetrySender sender = new TelemetrySender();
|
||||
|
||||
sender.send(this.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.common;
|
||||
|
||||
import com.azure.data.cosmos.CosmosResponse;
|
||||
import com.azure.data.cosmos.CosmosResponseDiagnostics;
|
||||
import com.azure.data.cosmos.FeedResponse;
|
||||
import com.azure.data.cosmos.FeedResponseDiagnostics;
|
||||
import com.azure.data.cosmos.Resource;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.ResponseDiagnostics;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.ResponseDiagnosticsProcessor;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.convert.ObjectMapperFactory;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.exception.ConfigurationException;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Slf4j
|
||||
public class CosmosdbUtils {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getCopyFrom(@NonNull T instance) {
|
||||
final ObjectMapper mapper = ObjectMapperFactory.getObjectMapper();
|
||||
|
||||
try {
|
||||
final String s = mapper.writeValueAsString(instance);
|
||||
return (T) mapper.readValue(s, instance.getClass());
|
||||
} catch (IOException e) {
|
||||
throw new ConfigurationException("failed to get copy from " + instance.getClass().getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T extends Resource> void fillAndProcessResponseDiagnostics(
|
||||
ResponseDiagnosticsProcessor responseDiagnosticsProcessor,
|
||||
CosmosResponse<T> cosmosResponse, FeedResponse<T> feedResponse) {
|
||||
if (responseDiagnosticsProcessor == null) {
|
||||
return;
|
||||
}
|
||||
CosmosResponseDiagnostics cosmosResponseDiagnostics = null;
|
||||
if (cosmosResponse != null) {
|
||||
cosmosResponseDiagnostics = cosmosResponse.cosmosResponseDiagnosticsString();
|
||||
}
|
||||
FeedResponseDiagnostics feedResponseDiagnostics = null;
|
||||
ResponseDiagnostics.CosmosResponseStatistics cosmosResponseStatistics = null;
|
||||
if (feedResponse != null) {
|
||||
feedResponseDiagnostics = feedResponse.feedResponseDiagnostics();
|
||||
cosmosResponseStatistics = new ResponseDiagnostics.CosmosResponseStatistics(feedResponse);
|
||||
}
|
||||
if (cosmosResponseDiagnostics == null &&
|
||||
(feedResponseDiagnostics == null || feedResponseDiagnostics.toString().isEmpty()) &&
|
||||
cosmosResponseStatistics == null) {
|
||||
log.debug("Empty response diagnostics");
|
||||
return;
|
||||
}
|
||||
final ResponseDiagnostics responseDiagnostics =
|
||||
new ResponseDiagnostics(cosmosResponseDiagnostics, feedResponseDiagnostics, cosmosResponseStatistics);
|
||||
|
||||
// Process response diagnostics
|
||||
responseDiagnosticsProcessor.processResponseDiagnostics(responseDiagnostics);
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.common;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.config.EmbeddedValueResolver;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Domenico Sibilio
|
||||
*
|
||||
*/
|
||||
public class ExpressionResolver {
|
||||
|
||||
private static EmbeddedValueResolver embeddedValueResolver;
|
||||
|
||||
public ExpressionResolver(BeanFactory beanFactory) {
|
||||
if (beanFactory instanceof ConfigurableBeanFactory) {
|
||||
setEmbeddedValueResolver(new EmbeddedValueResolver((ConfigurableBeanFactory) beanFactory));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the given string value via an {@link EmbeddedValueResolver}
|
||||
* @param expression the expression to be resolved
|
||||
* @return the resolved expression, may be {@literal null}
|
||||
*/
|
||||
public static String resolveExpression(String expression) {
|
||||
return embeddedValueResolver != null
|
||||
? embeddedValueResolver.resolveStringValue(expression)
|
||||
: expression;
|
||||
}
|
||||
|
||||
private static void setEmbeddedValueResolver(EmbeddedValueResolver embeddedValueResolver) {
|
||||
ExpressionResolver.embeddedValueResolver = embeddedValueResolver;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
/*
|
||||
* Disclaimer:
|
||||
* This class is copied from https://github.com/Microsoft/azure-tools-for-java/ with minor modification (fixing
|
||||
* static analysis error).
|
||||
* Location in the repo: /Utils/azuretools-core/src/com/microsoft/azuretools/azurecommons/util/MacAddress.java
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.common;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Cleanup;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class MacAddress {
|
||||
|
||||
private static final String UNKNOWN_MAC_ADDRESS = "Unknown-Mac-Address";
|
||||
private static final String MAC_REGEX = "([0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}";
|
||||
private static final String MAC_REGEX_ZERO = "([0]{2}[:-]){5}[0]{2}";
|
||||
private static final String HASHED_MAC_REGEX = "[0-9a-f]{64}";
|
||||
|
||||
private static boolean isValidHashMacFormat(@NonNull String hashMac) {
|
||||
if (hashMac.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Pattern.compile(HASHED_MAC_REGEX).matcher(hashMac).matches();
|
||||
}
|
||||
|
||||
private static String getRawMac() {
|
||||
final List<String> commands;
|
||||
final String os = System.getProperty("os.name");
|
||||
final StringBuilder macBuilder = new StringBuilder();
|
||||
|
||||
if (os != null && !os.isEmpty() && os.toLowerCase(Locale.US).startsWith("win")) {
|
||||
commands = Collections.singletonList("getmac");
|
||||
} else {
|
||||
commands = Arrays.asList("ifconfig", "-a");
|
||||
}
|
||||
|
||||
try {
|
||||
String tmp;
|
||||
final ProcessBuilder builder = new ProcessBuilder(commands);
|
||||
final Process process = builder.start();
|
||||
@Cleanup final InputStreamReader streamReader = new InputStreamReader(process.getInputStream(),
|
||||
StandardCharsets.UTF_8);
|
||||
@Cleanup final BufferedReader reader = new BufferedReader(streamReader);
|
||||
|
||||
while ((tmp = reader.readLine()) != null) {
|
||||
macBuilder.append(tmp);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return macBuilder.toString();
|
||||
}
|
||||
|
||||
private static String getHexDigest(byte digest) {
|
||||
final String hex = Integer.toString((digest & 0xff) + 0x100, 16);
|
||||
|
||||
return hex.substring(1);
|
||||
}
|
||||
|
||||
private static String hash(@NonNull String mac) {
|
||||
if (mac.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
||||
try {
|
||||
final MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
|
||||
|
||||
messageDigest.update(mac.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
final byte[] digestBytes = messageDigest.digest();
|
||||
|
||||
for (final byte digest : digestBytes) {
|
||||
builder.append(getHexDigest(digest));
|
||||
}
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
return "";
|
||||
}
|
||||
|
||||
Assert.isTrue(isValidHashMacFormat(builder.toString()), "Invalid format for HashMac");
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static String getHashMac() {
|
||||
final String rawMac = getRawMac();
|
||||
|
||||
if (rawMac.isEmpty()) {
|
||||
return UNKNOWN_MAC_ADDRESS;
|
||||
}
|
||||
|
||||
final Pattern pattern = Pattern.compile(MAC_REGEX);
|
||||
final Pattern patternZero = Pattern.compile(MAC_REGEX_ZERO);
|
||||
final Matcher matcher = pattern.matcher(rawMac);
|
||||
|
||||
String mac = "";
|
||||
|
||||
while (matcher.find()) {
|
||||
mac = matcher.group(0);
|
||||
|
||||
if (!patternZero.matcher(mac).matches()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final String hashMac = hash(mac);
|
||||
|
||||
if (StringUtils.hasText(hashMac)) {
|
||||
return hashMac;
|
||||
}
|
||||
|
||||
return UNKNOWN_MAC_ADDRESS;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.common;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Memoize function computation results
|
||||
*/
|
||||
public class Memoizer<I, O> {
|
||||
|
||||
private final Map<I, O> cache = new ConcurrentHashMap<>();
|
||||
|
||||
private Memoizer() {}
|
||||
|
||||
public static <I, O> Function<I, O> memoize(Function<I, O> function) {
|
||||
return new Memoizer<I, O>().internalMemoize(function);
|
||||
}
|
||||
|
||||
private Function<I, O> internalMemoize(Function<I, O> function) {
|
||||
return input -> cache.computeIfAbsent(input, function);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.common;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class PropertyLoader {
|
||||
|
||||
private static final String PROJECT_PROPERTY_FILE = "/META-INF/project.properties";
|
||||
|
||||
private static final String APPLICATION_PROPERTY_FILE = "/application.properties";
|
||||
|
||||
private static final String APPLICATION_YML_FILE = "/application.yml";
|
||||
|
||||
private static final String TELEMETRY_CONFIG_FILE = "/telemetry.config";
|
||||
|
||||
public static String getProjectVersion() {
|
||||
return getPropertyByName("project.version", PROJECT_PROPERTY_FILE);
|
||||
}
|
||||
|
||||
public static String getTelemetryInstrumentationKey() {
|
||||
return getPropertyByName("telemetry.instrumentationKey", TELEMETRY_CONFIG_FILE);
|
||||
}
|
||||
|
||||
public static boolean isApplicationTelemetryAllowed() {
|
||||
String allowed = getPropertyByName("cosmosdb.telemetryAllowed", APPLICATION_PROPERTY_FILE);
|
||||
|
||||
if (allowed == null) {
|
||||
allowed = getPropertyByName("telemetryAllowed", APPLICATION_YML_FILE);
|
||||
}
|
||||
|
||||
// Default, no telemetry
|
||||
if (allowed == null) {
|
||||
return false;
|
||||
} else {
|
||||
return !allowed.equalsIgnoreCase("false");
|
||||
}
|
||||
}
|
||||
|
||||
private static String getPropertyByName(@NonNull String name, @NonNull String filename) {
|
||||
final Properties properties = new Properties();
|
||||
final InputStream inputStream = PropertyLoader.class.getResourceAsStream(filename);
|
||||
|
||||
if (inputStream == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
properties.load(inputStream);
|
||||
} catch (IOException e) {
|
||||
// Omitted
|
||||
} finally {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
// Omitted
|
||||
}
|
||||
}
|
||||
|
||||
return properties.getProperty(name);
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.common;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
|
||||
@Getter
|
||||
public class TelemetryEventData {
|
||||
|
||||
private final String name;
|
||||
|
||||
@JsonProperty("iKey")
|
||||
private final String instrumentationKey;
|
||||
|
||||
private final Tags tags = new Tags("Spring-on-azure", "Java-maven-plugin");
|
||||
|
||||
private final EventData data = new EventData("EventData");
|
||||
|
||||
private final String time;
|
||||
|
||||
public TelemetryEventData(String eventName, @NonNull Map<String, String> properties) {
|
||||
Assert.hasText(eventName, "Event name should contain text.");
|
||||
|
||||
name = "Microsoft.ApplicationInsights.Event";
|
||||
instrumentationKey = PropertyLoader.getTelemetryInstrumentationKey();
|
||||
|
||||
data.getBaseData().setName(eventName);
|
||||
data.getBaseData().setProperties(properties);
|
||||
time = Instant.now().toString();
|
||||
}
|
||||
|
||||
@Getter
|
||||
private static class Tags {
|
||||
|
||||
@JsonProperty("ai.cloud.roleInstance")
|
||||
private final String aiCloudRoleInstance;
|
||||
|
||||
@JsonProperty("ai.internal.sdkVersion")
|
||||
private final String aiInternalSdkVersion;
|
||||
|
||||
public Tags(String instance, String sdkVersion) {
|
||||
aiCloudRoleInstance = instance;
|
||||
aiInternalSdkVersion = sdkVersion;
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
private static class EventData {
|
||||
|
||||
private final String baseType;
|
||||
|
||||
private final CustomData baseData = new CustomData();
|
||||
|
||||
public EventData(String baseType) {
|
||||
this.baseType = baseType;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter(AccessLevel.PRIVATE)
|
||||
private static class CustomData {
|
||||
|
||||
private final Integer ver = 2;
|
||||
|
||||
private String name;
|
||||
|
||||
private Map<String, String> properties;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.common;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON;
|
||||
|
||||
@Slf4j
|
||||
public class TelemetrySender {
|
||||
|
||||
private static final String PROPERTY_INSTALLATION_ID = "installationId";
|
||||
|
||||
private static final String PROPERTY_VERSION = "version";
|
||||
|
||||
private static final String PROPERTY_SERVICE_NAME = "serviceName";
|
||||
|
||||
private static final String PROJECT_INFO = "spring-data-cosmosdb/" + PropertyLoader.getProjectVersion();
|
||||
|
||||
private static final String TELEMETRY_TARGET_URL = "https://dc.services.visualstudio.com/v2/track";
|
||||
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
|
||||
private static final int RETRY_LIMIT = 3; // Align the retry times with sdk
|
||||
|
||||
private ResponseEntity<String> executeRequest(final TelemetryEventData eventData) {
|
||||
final HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
headers.add(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON.toString());
|
||||
|
||||
try {
|
||||
final RestTemplate restTemplate = new RestTemplate();
|
||||
final HttpEntity<String> body = new HttpEntity<>(MAPPER.writeValueAsString(eventData), headers);
|
||||
|
||||
return restTemplate.exchange(TELEMETRY_TARGET_URL, HttpMethod.POST, body, String.class);
|
||||
} catch (JsonProcessingException | HttpClientErrorException ignore) {
|
||||
log.warn("Failed to exchange telemetry request, {}.", ignore.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void sendTelemetryData(@NonNull TelemetryEventData eventData) {
|
||||
ResponseEntity<String> response = null;
|
||||
|
||||
for (int i = 0; i < RETRY_LIMIT; i++) {
|
||||
response = executeRequest(eventData);
|
||||
|
||||
if (response != null && response.getStatusCode() == HttpStatus.OK) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (response != null && response.getStatusCode() != HttpStatus.OK) {
|
||||
log.warn("Failed to send telemetry data, response status code {}.", response.getStatusCode().toString());
|
||||
}
|
||||
}
|
||||
|
||||
public void send(String name) {
|
||||
Assert.hasText(name, "Event name should contain text.");
|
||||
|
||||
sendTelemetryData(new TelemetryEventData(name, getProperties()));
|
||||
}
|
||||
|
||||
private Map<String, String> getProperties() {
|
||||
final Map<String, String> properties = new HashMap<>();
|
||||
|
||||
properties.put(PROPERTY_VERSION, PROJECT_INFO);
|
||||
properties.put(PROPERTY_SERVICE_NAME, "cosmosdb");
|
||||
properties.put(PROPERTY_INSTALLATION_ID, MacAddress.getHashMac());
|
||||
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.config;
|
||||
|
||||
import com.azure.data.cosmos.CosmosClient;
|
||||
import com.azure.data.cosmos.sync.CosmosSyncClient;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.Constants;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.CosmosDbFactory;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.CosmosTemplate;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.ReactiveCosmosTemplate;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.convert.MappingCosmosConverter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public abstract class AbstractCosmosConfiguration extends CosmosConfigurationSupport {
|
||||
|
||||
@Bean
|
||||
public CosmosClient cosmosClient(CosmosDBConfig config) {
|
||||
return this.cosmosDbFactory(config).getCosmosClient();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CosmosSyncClient cosmosSyncClient(CosmosDBConfig config) {
|
||||
return this.cosmosDbFactory(config).getCosmosSyncClient();
|
||||
}
|
||||
|
||||
@Qualifier(Constants.OBJECTMAPPER_BEAN_NAME)
|
||||
@Autowired(required = false)
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Bean
|
||||
public CosmosDbFactory cosmosDbFactory(CosmosDBConfig config) {
|
||||
return new CosmosDbFactory(config);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CosmosTemplate cosmosTemplate(CosmosDBConfig config) throws ClassNotFoundException {
|
||||
return new CosmosTemplate(this.cosmosDbFactory(config), this.mappingCosmosConverter(),
|
||||
config.getDatabase());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ReactiveCosmosTemplate reactiveCosmosTemplate(CosmosDBConfig config) throws ClassNotFoundException {
|
||||
return new ReactiveCosmosTemplate(this.cosmosDbFactory(config), this.mappingCosmosConverter(),
|
||||
config.getDatabase());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MappingCosmosConverter mappingCosmosConverter() throws ClassNotFoundException {
|
||||
return new MappingCosmosConverter(this.cosmosMappingContext(), objectMapper);
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.config;
|
||||
|
||||
import com.microsoft.azure.spring.data.cosmosdb.common.ExpressionResolver;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.mapping.CosmosMappingContext;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
import org.springframework.data.annotation.Persistent;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class CosmosConfigurationSupport {
|
||||
|
||||
@Bean
|
||||
public ExpressionResolver expressionResolver(BeanFactory beanFactory) {
|
||||
return new ExpressionResolver(beanFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CosmosMappingContext cosmosMappingContext() throws ClassNotFoundException {
|
||||
final CosmosMappingContext mappingContext = new CosmosMappingContext();
|
||||
mappingContext.setInitialEntitySet(getInitialEntitySet());
|
||||
|
||||
return mappingContext;
|
||||
}
|
||||
|
||||
protected Collection<String> getMappingBasePackages() {
|
||||
final Package mappingBasePackage = getClass().getPackage();
|
||||
return Collections.singleton(mappingBasePackage == null ? null : mappingBasePackage.getName());
|
||||
}
|
||||
|
||||
protected Set<Class<?>> getInitialEntitySet() throws ClassNotFoundException {
|
||||
final Set<Class<?>> initialEntitySet = new HashSet<>();
|
||||
|
||||
for (final String basePackage : getMappingBasePackages()) {
|
||||
initialEntitySet.addAll(scanForEntities(basePackage));
|
||||
}
|
||||
|
||||
return initialEntitySet;
|
||||
}
|
||||
|
||||
protected Set<Class<?>> scanForEntities(String basePackage) throws ClassNotFoundException {
|
||||
if (!StringUtils.hasText(basePackage)) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
final Set<Class<?>> initialEntitySet = new HashSet<>();
|
||||
|
||||
if (StringUtils.hasText(basePackage)) {
|
||||
final ClassPathScanningCandidateComponentProvider componentProvider =
|
||||
new ClassPathScanningCandidateComponentProvider(false);
|
||||
|
||||
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class));
|
||||
|
||||
for (final BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {
|
||||
final String className = candidate.getBeanClassName();
|
||||
Assert.notNull(className, "Bean class name is null.");
|
||||
|
||||
initialEntitySet
|
||||
.add(ClassUtils.forName(className, CosmosConfigurationSupport.class.getClassLoader()));
|
||||
}
|
||||
}
|
||||
|
||||
return initialEntitySet;
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.config;
|
||||
|
||||
import com.azure.data.cosmos.ConnectionPolicy;
|
||||
import com.azure.data.cosmos.ConsistencyLevel;
|
||||
import com.azure.data.cosmos.CosmosKeyCredential;
|
||||
import com.azure.data.cosmos.internal.RequestOptions;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.ResponseDiagnosticsProcessor;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.exception.CosmosDBAccessException;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@Getter
|
||||
@Builder(builderMethodName = "defaultBuilder")
|
||||
public class CosmosDBConfig {
|
||||
private String uri;
|
||||
|
||||
private String key;
|
||||
|
||||
private String database;
|
||||
|
||||
private ConnectionPolicy connectionPolicy;
|
||||
|
||||
private ConsistencyLevel consistencyLevel;
|
||||
|
||||
private boolean allowTelemetry;
|
||||
|
||||
private RequestOptions requestOptions;
|
||||
|
||||
private CosmosKeyCredential cosmosKeyCredential;
|
||||
|
||||
@Setter
|
||||
private ResponseDiagnosticsProcessor responseDiagnosticsProcessor;
|
||||
|
||||
@Setter
|
||||
private boolean populateQueryMetrics;
|
||||
|
||||
public static CosmosDBConfigBuilder builder(String uri, CosmosKeyCredential cosmosKeyCredential,
|
||||
String database) {
|
||||
return defaultBuilder()
|
||||
.uri(uri)
|
||||
.cosmosKeyCredential(cosmosKeyCredential)
|
||||
.database(database)
|
||||
.connectionPolicy(ConnectionPolicy.defaultPolicy())
|
||||
.consistencyLevel(ConsistencyLevel.SESSION)
|
||||
.requestOptions(new RequestOptions());
|
||||
}
|
||||
|
||||
public static CosmosDBConfigBuilder builder(String uri, String key, String database) {
|
||||
return defaultBuilder()
|
||||
.uri(uri)
|
||||
.key(key)
|
||||
.database(database)
|
||||
.connectionPolicy(ConnectionPolicy.defaultPolicy())
|
||||
.consistencyLevel(ConsistencyLevel.SESSION)
|
||||
.requestOptions(new RequestOptions());
|
||||
}
|
||||
|
||||
public static CosmosDBConfigBuilder builder(String connectionString, String database) {
|
||||
Assert.hasText(connectionString, "connection string should have text!");
|
||||
try {
|
||||
final String uri = connectionString.split(";")[0].split("=")[1];
|
||||
final String key = connectionString.split(";")[1].split("=")[1];
|
||||
return builder(uri, key, database);
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
throw new CosmosDBAccessException("could not parse connection string");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core;
|
||||
|
||||
import com.azure.data.cosmos.CosmosContainerProperties;
|
||||
import com.azure.data.cosmos.PartitionKey;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.convert.MappingCosmosConverter;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.query.DocumentQuery;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.repository.support.CosmosEntityInformation;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface CosmosOperations {
|
||||
|
||||
/**
|
||||
* Use getContainerName() instead
|
||||
* @param domainType class type
|
||||
* @return container name
|
||||
*/
|
||||
@Deprecated
|
||||
String getCollectionName(Class<?> domainType);
|
||||
|
||||
String getContainerName(Class<?> domainType);
|
||||
|
||||
/**
|
||||
* Use createContainerIfNotExists() instead
|
||||
* @param information cosmos entity information
|
||||
* @return created container properties
|
||||
*/
|
||||
@Deprecated
|
||||
CosmosContainerProperties createCollectionIfNotExists(CosmosEntityInformation<?, ?> information);
|
||||
|
||||
CosmosContainerProperties createContainerIfNotExists(CosmosEntityInformation<?, ?> information);
|
||||
|
||||
<T> List<T> findAll(Class<T> domainType);
|
||||
|
||||
<T> List<T> findAll(String containerName, Class<T> domainType);
|
||||
|
||||
<T> List<T> findAll(PartitionKey partitionKey, Class<T> domainType);
|
||||
|
||||
<T> T findById(Object id, Class<T> domainType);
|
||||
|
||||
<T> T findById(String containerName, Object id, Class<T> domainType);
|
||||
|
||||
<T> T findById(Object id, Class<T> domainType, PartitionKey partitionKey);
|
||||
|
||||
<T> T insert(T objectToSave, PartitionKey partitionKey);
|
||||
|
||||
<T> T insert(String containerName, T objectToSave, PartitionKey partitionKey);
|
||||
|
||||
<T> void upsert(T object, PartitionKey partitionKey);
|
||||
|
||||
<T> void upsert(String containerName, T object, PartitionKey partitionKey);
|
||||
|
||||
<T> T upsertAndReturnEntity(String containerName, T object, PartitionKey partitionKey);
|
||||
|
||||
void deleteById(String containerName, Object id, PartitionKey partitionKey);
|
||||
|
||||
void deleteAll(String containerName, Class<?> domainType);
|
||||
|
||||
/**
|
||||
* Use deleteContainer() instead
|
||||
* @param containerName container name
|
||||
*/
|
||||
@Deprecated
|
||||
void deleteCollection(String containerName);
|
||||
|
||||
void deleteContainer(String containerName);
|
||||
|
||||
<T> List<T> delete(DocumentQuery query, Class<T> domainType, String containerName);
|
||||
|
||||
<T> List<T> find(DocumentQuery query, Class<T> domainType, String containerName);
|
||||
|
||||
<T, ID> List<T> findByIds(Iterable<ID> ids, Class<T> domainType, String containerName);
|
||||
|
||||
<T> Boolean exists(DocumentQuery query, Class<T> domainType, String containerName);
|
||||
|
||||
<T> Page<T> findAll(Pageable pageable, Class<T> domainType, String containerName);
|
||||
|
||||
<T> Page<T> paginationQuery(DocumentQuery query, Class<T> domainType, String containerName);
|
||||
|
||||
long count(String containerName);
|
||||
|
||||
<T> long count(DocumentQuery query, Class<T> domainType, String containerName);
|
||||
|
||||
MappingCosmosConverter getConverter();
|
||||
}
|
|
@ -1,628 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core;
|
||||
|
||||
import com.azure.data.cosmos.AccessCondition;
|
||||
import com.azure.data.cosmos.AccessConditionType;
|
||||
import com.azure.data.cosmos.CosmosClient;
|
||||
import com.azure.data.cosmos.CosmosContainerProperties;
|
||||
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.PartitionKey;
|
||||
import com.azure.data.cosmos.SqlQuerySpec;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.CosmosDbFactory;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.common.Memoizer;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.convert.MappingCosmosConverter;
|
||||
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.CosmosPageImpl;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.query.CosmosPageRequest;
|
||||
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.DocumentQuery;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.repository.support.CosmosEntityInformation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.data.domain.Page;
|
||||
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.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.microsoft.azure.spring.data.cosmosdb.common.CosmosdbUtils.fillAndProcessResponseDiagnostics;
|
||||
import static com.microsoft.azure.spring.data.cosmosdb.exception.CosmosDBExceptionUtils.exceptionHandler;
|
||||
import static com.microsoft.azure.spring.data.cosmosdb.exception.CosmosDBExceptionUtils.findAPIExceptionHandler;
|
||||
|
||||
@Slf4j
|
||||
public class CosmosTemplate implements CosmosOperations, ApplicationContextAware {
|
||||
|
||||
private static final String COUNT_VALUE_KEY = "_aggregate";
|
||||
|
||||
private final MappingCosmosConverter mappingCosmosConverter;
|
||||
private final String databaseName;
|
||||
private final ResponseDiagnosticsProcessor responseDiagnosticsProcessor;
|
||||
private final boolean isPopulateQueryMetrics;
|
||||
|
||||
private final CosmosClient cosmosClient;
|
||||
private Function<Class<?>, CosmosEntityInformation<?, ?>> entityInfoCreator =
|
||||
Memoizer.memoize(this::getCosmosEntityInformation);
|
||||
|
||||
public CosmosTemplate(CosmosDbFactory cosmosDbFactory,
|
||||
MappingCosmosConverter mappingCosmosConverter,
|
||||
String dbName) {
|
||||
Assert.notNull(cosmosDbFactory, "CosmosDbFactory must not be null!");
|
||||
Assert.notNull(mappingCosmosConverter, "MappingCosmosConverter must not be null!");
|
||||
|
||||
this.mappingCosmosConverter = mappingCosmosConverter;
|
||||
|
||||
this.databaseName = dbName;
|
||||
this.cosmosClient = cosmosDbFactory.getCosmosClient();
|
||||
this.responseDiagnosticsProcessor = cosmosDbFactory.getConfig().getResponseDiagnosticsProcessor();
|
||||
this.isPopulateQueryMetrics = cosmosDbFactory.getConfig().isPopulateQueryMetrics();
|
||||
}
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
}
|
||||
|
||||
public <T> T insert(T objectToSave, PartitionKey partitionKey) {
|
||||
Assert.notNull(objectToSave, "domainType should not be null");
|
||||
|
||||
return insert(getContainerName(objectToSave.getClass()), objectToSave, partitionKey);
|
||||
}
|
||||
|
||||
public <T> T insert(String containerName, T objectToSave, PartitionKey partitionKey) {
|
||||
Assert.hasText(containerName, "containerName should not be null, empty or only whitespaces");
|
||||
Assert.notNull(objectToSave, "objectToSave should not be null");
|
||||
|
||||
final CosmosItemProperties originalItem = mappingCosmosConverter.writeCosmosItemProperties(objectToSave);
|
||||
|
||||
log.debug("execute createItem in database {} container {}", this.databaseName, containerName);
|
||||
|
||||
final CosmosItemRequestOptions options = new CosmosItemRequestOptions();
|
||||
options.partitionKey(partitionKey);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final Class<T> domainType = (Class<T>) objectToSave.getClass();
|
||||
|
||||
final CosmosItemResponse response = cosmosClient
|
||||
.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.createItem(originalItem, options)
|
||||
.doOnNext(cosmosItemResponse -> fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
cosmosItemResponse, null))
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to insert item", throwable))
|
||||
.block();
|
||||
|
||||
assert response != null;
|
||||
return mappingCosmosConverter.read(domainType, response.properties());
|
||||
}
|
||||
|
||||
public <T> T findById(Object id, Class<T> domainType) {
|
||||
Assert.notNull(domainType, "domainType should not be null");
|
||||
|
||||
return findById(getContainerName(domainType), id, domainType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T findById(Object id, Class<T> domainType, PartitionKey partitionKey) {
|
||||
Assert.notNull(domainType, "domainType should not be null");
|
||||
Assert.notNull(partitionKey, "partitionKey should not be null");
|
||||
assertValidId(id);
|
||||
|
||||
final String containerName = getContainerName(domainType);
|
||||
return cosmosClient
|
||||
.getDatabase(databaseName)
|
||||
.getContainer(containerName)
|
||||
.getItem(id.toString(), partitionKey)
|
||||
.read()
|
||||
.flatMap(cosmosItemResponse -> {
|
||||
fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
cosmosItemResponse, null);
|
||||
return Mono.justOrEmpty(toDomainObject(domainType,
|
||||
cosmosItemResponse.properties()));
|
||||
})
|
||||
.onErrorResume(throwable ->
|
||||
findAPIExceptionHandler("Failed to find item", throwable))
|
||||
.block();
|
||||
}
|
||||
|
||||
public <T> T findById(String containerName, Object id, Class<T> domainType) {
|
||||
Assert.hasText(containerName, "containerName should not be null, empty or only whitespaces");
|
||||
Assert.notNull(domainType, "domainType should not be null");
|
||||
assertValidId(id);
|
||||
|
||||
final String query = String.format("select * from root where root.id = '%s'", id.toString());
|
||||
final FeedOptions options = new FeedOptions();
|
||||
options.enableCrossPartitionQuery(true);
|
||||
options.populateQueryMetrics(isPopulateQueryMetrics);
|
||||
return cosmosClient
|
||||
.getDatabase(databaseName)
|
||||
.getContainer(containerName)
|
||||
.queryItems(query, options)
|
||||
.flatMap(cosmosItemFeedResponse -> {
|
||||
fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
null, cosmosItemFeedResponse);
|
||||
return Mono.justOrEmpty(cosmosItemFeedResponse
|
||||
.results()
|
||||
.stream()
|
||||
.map(cosmosItem -> mappingCosmosConverter.read(domainType, cosmosItem))
|
||||
.findFirst());
|
||||
})
|
||||
.onErrorResume(throwable ->
|
||||
findAPIExceptionHandler("Failed to find item", throwable))
|
||||
.blockFirst();
|
||||
}
|
||||
|
||||
public <T> void upsert(T object, PartitionKey partitionKey) {
|
||||
Assert.notNull(object, "Upsert object should not be null");
|
||||
|
||||
upsert(getContainerName(object.getClass()), object, partitionKey);
|
||||
}
|
||||
|
||||
public <T> void upsert(String containerName, T object, PartitionKey partitionKey) {
|
||||
upsertAndReturnEntity(containerName, object, partitionKey);
|
||||
}
|
||||
|
||||
public <T> T upsertAndReturnEntity(String containerName, T object, PartitionKey partitionKey) {
|
||||
Assert.hasText(containerName, "containerName should not be null, empty or only whitespaces");
|
||||
Assert.notNull(object, "Upsert object should not be null");
|
||||
|
||||
final CosmosItemProperties originalItem = mappingCosmosConverter.writeCosmosItemProperties(object);
|
||||
|
||||
log.debug("execute upsert item in database {} container {}", this.databaseName, containerName);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final Class<T> domainType = (Class<T>) object.getClass();
|
||||
|
||||
final CosmosItemRequestOptions options = new CosmosItemRequestOptions();
|
||||
options.partitionKey(partitionKey);
|
||||
applyVersioning(domainType, originalItem, options);
|
||||
|
||||
final CosmosItemResponse cosmosItemResponse = cosmosClient
|
||||
.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.upsertItem(originalItem, options)
|
||||
.doOnNext(response -> fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
response, null))
|
||||
.onErrorResume(throwable -> exceptionHandler("Failed to upsert item", throwable))
|
||||
.block();
|
||||
|
||||
assert cosmosItemResponse != null;
|
||||
return mappingCosmosConverter.read(domainType, cosmosItemResponse.properties());
|
||||
}
|
||||
|
||||
public <T> List<T> findAll(Class<T> domainType) {
|
||||
Assert.notNull(domainType, "domainType should not be null");
|
||||
|
||||
return findAll(getContainerName(domainType), domainType);
|
||||
}
|
||||
|
||||
public <T> List<T> findAll(String containerName, final Class<T> domainType) {
|
||||
Assert.hasText(containerName, "containerName should not be null, empty or only whitespaces");
|
||||
Assert.notNull(domainType, "domainType should not be null");
|
||||
|
||||
final DocumentQuery query = new DocumentQuery(Criteria.getInstance(CriteriaType.ALL));
|
||||
|
||||
final List<CosmosItemProperties> items = findItems(query, domainType, containerName);
|
||||
return items.stream()
|
||||
.map(d -> getConverter().read(domainType, d))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> findAll(PartitionKey partitionKey, final Class<T> domainType) {
|
||||
Assert.notNull(partitionKey, "partitionKey should not be null");
|
||||
Assert.notNull(domainType, "domainType should not be null");
|
||||
|
||||
final String containerName = getContainerName(domainType);
|
||||
|
||||
final FeedOptions feedOptions = new FeedOptions();
|
||||
feedOptions.partitionKey(partitionKey);
|
||||
feedOptions.populateQueryMetrics(isPopulateQueryMetrics);
|
||||
|
||||
return cosmosClient
|
||||
.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.readAllItems(feedOptions)
|
||||
.flatMap(cosmosItemFeedResponse -> {
|
||||
fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
null, cosmosItemFeedResponse);
|
||||
return Flux.fromIterable(cosmosItemFeedResponse.results());
|
||||
})
|
||||
.map(cosmosItemProperties -> toDomainObject(domainType, cosmosItemProperties))
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to find items", throwable))
|
||||
.collectList()
|
||||
.block();
|
||||
}
|
||||
|
||||
public void deleteAll(@NonNull String containerName, @NonNull Class<?> domainType) {
|
||||
Assert.hasText(containerName, "containerName should not be null, empty or only whitespaces");
|
||||
|
||||
final DocumentQuery query = new DocumentQuery(Criteria.getInstance(CriteriaType.ALL));
|
||||
|
||||
this.delete(query, domainType, containerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteCollection(@NonNull String containerName) {
|
||||
deleteContainer(containerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteContainer(@NonNull String containerName) {
|
||||
Assert.hasText(containerName, "containerName should have text.");
|
||||
cosmosClient.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.delete()
|
||||
.doOnNext(response -> fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
response, null))
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to delete container", throwable))
|
||||
.block();
|
||||
}
|
||||
|
||||
public String getCollectionName(Class<?> domainType) {
|
||||
return getContainerName(domainType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContainerName(Class<?> domainType) {
|
||||
Assert.notNull(domainType, "domainType should not be null");
|
||||
|
||||
return entityInfoCreator.apply(domainType).getContainerName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CosmosContainerProperties createCollectionIfNotExists(@NonNull CosmosEntityInformation<?, ?> information) {
|
||||
return createContainerIfNotExists(information);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CosmosContainerProperties createContainerIfNotExists(CosmosEntityInformation<?, ?> information) {
|
||||
final CosmosContainerResponse response = cosmosClient
|
||||
.createDatabaseIfNotExists(this.databaseName)
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to create database", throwable))
|
||||
.flatMap(cosmosDatabaseResponse -> {
|
||||
fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
cosmosDatabaseResponse, null);
|
||||
final CosmosContainerProperties cosmosContainerProperties = new CosmosContainerProperties(
|
||||
information.getContainerName(),
|
||||
"/" + information.getPartitionKeyFieldName());
|
||||
cosmosContainerProperties.defaultTimeToLive(information.getTimeToLive());
|
||||
cosmosContainerProperties.indexingPolicy(information.getIndexingPolicy());
|
||||
return cosmosDatabaseResponse
|
||||
.database()
|
||||
.createContainerIfNotExists(cosmosContainerProperties, information.getRequestUnit())
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to create container", throwable))
|
||||
.doOnNext(cosmosContainerResponse ->
|
||||
fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
cosmosContainerResponse, null));
|
||||
})
|
||||
.block();
|
||||
assert response != null;
|
||||
return response.properties();
|
||||
}
|
||||
|
||||
public void deleteById(String containerName, Object id, PartitionKey partitionKey) {
|
||||
Assert.hasText(containerName, "containerName should not be null, empty or only whitespaces");
|
||||
assertValidId(id);
|
||||
|
||||
log.debug("execute deleteById in database {} container {}", this.databaseName, containerName);
|
||||
|
||||
if (partitionKey == null) {
|
||||
partitionKey = PartitionKey.None;
|
||||
}
|
||||
final CosmosItemRequestOptions options = new CosmosItemRequestOptions();
|
||||
options.partitionKey(partitionKey);
|
||||
cosmosClient.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.getItem(id.toString(), partitionKey)
|
||||
.delete(options)
|
||||
.doOnNext(response -> fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
response, null))
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to delete item", throwable))
|
||||
.block();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, ID> List<T> findByIds(Iterable<ID> ids, Class<T> domainType, String containerName) {
|
||||
Assert.notNull(ids, "Id list should not be null");
|
||||
Assert.notNull(domainType, "domainType should not be null.");
|
||||
Assert.hasText(containerName, "container should not be null, empty or only whitespaces");
|
||||
|
||||
final DocumentQuery query = new DocumentQuery(Criteria.getInstance(CriteriaType.IN, "id",
|
||||
Collections.singletonList(ids)));
|
||||
return find(query, domainType, containerName);
|
||||
}
|
||||
|
||||
public <T> List<T> find(@NonNull DocumentQuery query, @NonNull Class<T> domainType, String containerName) {
|
||||
Assert.notNull(query, "DocumentQuery should not be null.");
|
||||
Assert.notNull(domainType, "domainType should not be null.");
|
||||
Assert.hasText(containerName, "container should not be null, empty or only whitespaces");
|
||||
|
||||
return findItems(query, domainType, containerName)
|
||||
.stream()
|
||||
.map(cosmosItemProperties -> toDomainObject(domainType, cosmosItemProperties))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public <T> Boolean exists(@NonNull DocumentQuery query, @NonNull Class<T> domainType, String containerName) {
|
||||
return this.find(query, domainType, containerName).size() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the DocumentQuery, need to query the domains at first, then delete the item
|
||||
* from the result.
|
||||
* The cosmosdb Sql API do _NOT_ support DELETE query, we cannot add one DeleteQueryGenerator.
|
||||
*
|
||||
* @param query The representation for query method.
|
||||
* @param domainType Class of domain
|
||||
* @param containerName Container name of database
|
||||
* @param <T>
|
||||
* @return All the deleted items as List.
|
||||
*/
|
||||
@Override
|
||||
public <T> List<T> delete(@NonNull DocumentQuery query, @NonNull Class<T> domainType,
|
||||
@NonNull String containerName) {
|
||||
Assert.notNull(query, "DocumentQuery should not be null.");
|
||||
Assert.notNull(domainType, "domainType should not be null.");
|
||||
Assert.hasText(containerName, "container should not be null, empty or only whitespaces");
|
||||
|
||||
final List<CosmosItemProperties> results = findItems(query, domainType, containerName);
|
||||
final List<String> partitionKeyName = getPartitionKeyNames(domainType);
|
||||
|
||||
return results.stream().map(cosmosItemProperties -> {
|
||||
final CosmosItemResponse cosmosItemResponse = deleteItem(cosmosItemProperties,
|
||||
partitionKeyName, containerName, domainType);
|
||||
return getConverter().read(domainType, cosmosItemResponse.properties());
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Page<T> findAll(Pageable pageable, Class<T> domainType, String containerName) {
|
||||
final DocumentQuery query = new DocumentQuery(Criteria.getInstance(CriteriaType.ALL)).with(pageable);
|
||||
if (pageable.getSort().isSorted()) {
|
||||
query.with(pageable.getSort());
|
||||
}
|
||||
|
||||
return paginationQuery(query, domainType, containerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Page<T> paginationQuery(DocumentQuery query, Class<T> domainType, String containerName) {
|
||||
Assert.isTrue(query.getPageable().getPageSize() > 0, "pageable should have page size larger than 0");
|
||||
Assert.hasText(containerName, "container should not be null, empty or only whitespaces");
|
||||
|
||||
final Pageable pageable = query.getPageable();
|
||||
final FeedOptions feedOptions = new FeedOptions();
|
||||
if (pageable instanceof CosmosPageRequest) {
|
||||
feedOptions.requestContinuation(((CosmosPageRequest) pageable).getRequestContinuation());
|
||||
}
|
||||
|
||||
feedOptions.maxItemCount(pageable.getPageSize());
|
||||
feedOptions.enableCrossPartitionQuery(query.isCrossPartitionQuery(getPartitionKeyNames(domainType)));
|
||||
feedOptions.populateQueryMetrics(isPopulateQueryMetrics);
|
||||
|
||||
final SqlQuerySpec sqlQuerySpec = new FindQuerySpecGenerator().generateCosmos(query);
|
||||
final FeedResponse<CosmosItemProperties> feedResponse = cosmosClient
|
||||
.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.queryItems(sqlQuerySpec, feedOptions)
|
||||
.doOnNext(propertiesFeedResponse -> fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
null, propertiesFeedResponse))
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to query items", throwable))
|
||||
.next()
|
||||
.block();
|
||||
|
||||
assert feedResponse != null;
|
||||
final Iterator<CosmosItemProperties> it = feedResponse.results().iterator();
|
||||
|
||||
final List<T> result = new ArrayList<>();
|
||||
for (int index = 0; it.hasNext() && index < pageable.getPageSize(); index++) {
|
||||
|
||||
final CosmosItemProperties cosmosItemProperties = it.next();
|
||||
if (cosmosItemProperties == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final T entity = mappingCosmosConverter.read(domainType, cosmosItemProperties);
|
||||
result.add(entity);
|
||||
}
|
||||
|
||||
final long total = count(query, domainType, containerName);
|
||||
final int contentSize = result.size();
|
||||
|
||||
int pageSize;
|
||||
|
||||
if (contentSize < pageable.getPageSize() && contentSize > 0) {
|
||||
// If the content size is less than page size,
|
||||
// this means, cosmosDB is returning less items than page size,
|
||||
// because of either RU limit, or payload limit
|
||||
|
||||
// Set the page size to content size.
|
||||
pageSize = contentSize;
|
||||
} else {
|
||||
pageSize = pageable.getPageSize();
|
||||
}
|
||||
|
||||
final CosmosPageRequest pageRequest = CosmosPageRequest.of(pageable.getOffset(),
|
||||
pageable.getPageNumber(),
|
||||
pageSize,
|
||||
feedResponse.continuationToken(),
|
||||
query.getSort());
|
||||
|
||||
return new CosmosPageImpl<>(result, pageRequest, total);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(String containerName) {
|
||||
Assert.hasText(containerName, "container name should not be empty");
|
||||
|
||||
final DocumentQuery query = new DocumentQuery(Criteria.getInstance(CriteriaType.ALL));
|
||||
final Long count = getCountValue(query, true, containerName);
|
||||
assert count != null;
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> long count(DocumentQuery query, Class<T> domainType, String containerName) {
|
||||
Assert.notNull(domainType, "domainType should not be null");
|
||||
Assert.hasText(containerName, "container name should not be empty");
|
||||
|
||||
final boolean isCrossPartitionQuery =
|
||||
query.isCrossPartitionQuery(getPartitionKeyNames(domainType));
|
||||
final Long count = getCountValue(query, isCrossPartitionQuery, containerName);
|
||||
assert count != null;
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingCosmosConverter getConverter() {
|
||||
return this.mappingCosmosConverter;
|
||||
}
|
||||
|
||||
private Long getCountValue(DocumentQuery query, boolean isCrossPartitionQuery, String containerName) {
|
||||
final SqlQuerySpec querySpec = new CountQueryGenerator().generateCosmos(query);
|
||||
final FeedOptions options = new FeedOptions();
|
||||
|
||||
options.enableCrossPartitionQuery(isCrossPartitionQuery);
|
||||
options.populateQueryMetrics(isPopulateQueryMetrics);
|
||||
|
||||
return executeQuery(querySpec, containerName, options)
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to get count value", throwable))
|
||||
.doOnNext(response -> fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
null, response))
|
||||
.next()
|
||||
.map(r -> r.results().get(0).getLong(COUNT_VALUE_KEY))
|
||||
.block();
|
||||
}
|
||||
|
||||
private Flux<FeedResponse<CosmosItemProperties>> executeQuery(SqlQuerySpec sqlQuerySpec, String containerName,
|
||||
FeedOptions options) {
|
||||
return cosmosClient.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.queryItems(sqlQuerySpec, options)
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to execute query", throwable));
|
||||
}
|
||||
|
||||
private List<String> getPartitionKeyNames(Class<?> domainType) {
|
||||
final CosmosEntityInformation<?, ?> entityInfo = entityInfoCreator.apply(domainType);
|
||||
|
||||
if (entityInfo.getPartitionKeyFieldName() == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
return Collections.singletonList(entityInfo.getPartitionKeyFieldName());
|
||||
}
|
||||
|
||||
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> findItems(@NonNull DocumentQuery query,
|
||||
@NonNull Class<?> domainType,
|
||||
@NonNull String containerName) {
|
||||
final SqlQuerySpec sqlQuerySpec = new FindQuerySpecGenerator().generateCosmos(query);
|
||||
final boolean isCrossPartitionQuery =
|
||||
query.isCrossPartitionQuery(getPartitionKeyNames(domainType));
|
||||
final FeedOptions feedOptions = new FeedOptions();
|
||||
feedOptions.enableCrossPartitionQuery(isCrossPartitionQuery);
|
||||
feedOptions.populateQueryMetrics(isPopulateQueryMetrics);
|
||||
|
||||
return cosmosClient
|
||||
.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.queryItems(sqlQuerySpec, feedOptions)
|
||||
.flatMap(cosmosItemFeedResponse -> {
|
||||
fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
null, cosmosItemFeedResponse);
|
||||
return Flux.fromIterable(cosmosItemFeedResponse.results());
|
||||
})
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to find items", throwable))
|
||||
.collectList()
|
||||
.block();
|
||||
}
|
||||
|
||||
private CosmosItemResponse deleteItem(@NonNull CosmosItemProperties cosmosItemProperties,
|
||||
@NonNull List<String> partitionKeyNames,
|
||||
String containerName,
|
||||
@NonNull Class<?> domainType) {
|
||||
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)));
|
||||
}
|
||||
|
||||
if (partitionKey == null) {
|
||||
partitionKey = PartitionKey.None;
|
||||
}
|
||||
|
||||
final CosmosItemRequestOptions options = new CosmosItemRequestOptions(partitionKey);
|
||||
applyVersioning(domainType, cosmosItemProperties, options);
|
||||
|
||||
return cosmosClient
|
||||
.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.getItem(cosmosItemProperties.id(), partitionKey)
|
||||
.delete(options)
|
||||
.doOnNext(response -> fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
response, null))
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to delete item", throwable))
|
||||
.block();
|
||||
}
|
||||
|
||||
private <T> T toDomainObject(@NonNull Class<T> domainType, CosmosItemProperties cosmosItemProperties) {
|
||||
return mappingCosmosConverter.read(domainType, cosmosItemProperties);
|
||||
}
|
||||
|
||||
private void applyVersioning(Class<?> domainType,
|
||||
CosmosItemProperties cosmosItemProperties,
|
||||
CosmosItemRequestOptions options) {
|
||||
|
||||
if (entityInfoCreator.apply(domainType).isVersioned()) {
|
||||
final AccessCondition accessCondition = new AccessCondition();
|
||||
accessCondition.type(AccessConditionType.IF_MATCH);
|
||||
accessCondition.condition(cosmosItemProperties.etag());
|
||||
options.accessCondition(accessCondition);
|
||||
}
|
||||
}
|
||||
|
||||
private CosmosEntityInformation<?, ?> getCosmosEntityInformation(Class<?> domainType) {
|
||||
return new CosmosEntityInformation<>(domainType);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core;
|
||||
|
||||
import com.azure.data.cosmos.CosmosContainerResponse;
|
||||
import com.azure.data.cosmos.PartitionKey;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.convert.MappingCosmosConverter;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.query.DocumentQuery;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.repository.support.CosmosEntityInformation;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface ReactiveCosmosOperations {
|
||||
|
||||
String getContainerName(Class<?> domainType);
|
||||
|
||||
/**
|
||||
* Use createContainerIfNotExists() instead
|
||||
* @param information cosmos entity information
|
||||
* @return Mono of cosmos container response
|
||||
*/
|
||||
@Deprecated
|
||||
Mono<CosmosContainerResponse> createCollectionIfNotExists(CosmosEntityInformation information);
|
||||
|
||||
Mono<CosmosContainerResponse> createContainerIfNotExists(CosmosEntityInformation information);
|
||||
|
||||
<T> Flux<T> findAll(String containerName, Class<T> domainType);
|
||||
|
||||
<T> Flux<T> findAll(Class<T> domainType);
|
||||
|
||||
<T> Flux<T> findAll(PartitionKey partitionKey, Class<T> domainType);
|
||||
|
||||
<T> Mono<T> findById(Object id, Class<T> domainType);
|
||||
|
||||
<T> Mono<T> findById(String containerName, Object id, Class<T> domainType);
|
||||
|
||||
<T> Mono<T> findById(Object id, Class<T> domainType, PartitionKey partitionKey);
|
||||
|
||||
<T> Mono<T> insert(T objectToSave, PartitionKey partitionKey);
|
||||
|
||||
<T> Mono<T> insert(String containerName, Object objectToSave, PartitionKey partitionKey);
|
||||
|
||||
<T> Mono<T> upsert(T object, PartitionKey partitionKey);
|
||||
|
||||
<T> Mono<T> upsert(String containerName, T object, PartitionKey partitionKey);
|
||||
|
||||
Mono<Void> deleteById(String containerName, Object id, PartitionKey partitionKey);
|
||||
|
||||
Mono<Void> deleteAll(String containerName, String partitionKey);
|
||||
|
||||
void deleteContainer(String containerName);
|
||||
|
||||
<T> Flux<T> delete(DocumentQuery query, Class<T> domainType, String containerName);
|
||||
|
||||
<T> Flux<T> find(DocumentQuery query, Class<T> domainType, String containerName);
|
||||
|
||||
Mono<Boolean> exists(DocumentQuery query, Class<?> domainType, String containerName);
|
||||
|
||||
Mono<Boolean> existsById(Object id, Class<?> domainType, String containerName);
|
||||
|
||||
Mono<Long> count(String containerName);
|
||||
|
||||
Mono<Long> count(DocumentQuery query, String containerName);
|
||||
|
||||
MappingCosmosConverter getConverter();
|
||||
}
|
|
@ -1,684 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core;
|
||||
|
||||
import com.azure.data.cosmos.AccessCondition;
|
||||
import com.azure.data.cosmos.AccessConditionType;
|
||||
import com.azure.data.cosmos.CosmosClient;
|
||||
import com.azure.data.cosmos.CosmosContainerProperties;
|
||||
import com.azure.data.cosmos.CosmosContainerResponse;
|
||||
import com.azure.data.cosmos.CosmosItemProperties;
|
||||
import com.azure.data.cosmos.CosmosItemRequestOptions;
|
||||
import com.azure.data.cosmos.FeedOptions;
|
||||
import com.azure.data.cosmos.FeedResponse;
|
||||
import com.azure.data.cosmos.PartitionKey;
|
||||
import com.azure.data.cosmos.SqlQuerySpec;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.CosmosDbFactory;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.common.Memoizer;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.convert.MappingCosmosConverter;
|
||||
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.DocumentQuery;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.repository.support.CosmosEntityInformation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
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.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static com.microsoft.azure.spring.data.cosmosdb.common.CosmosdbUtils.fillAndProcessResponseDiagnostics;
|
||||
import static com.microsoft.azure.spring.data.cosmosdb.exception.CosmosDBExceptionUtils.exceptionHandler;
|
||||
import static com.microsoft.azure.spring.data.cosmosdb.exception.CosmosDBExceptionUtils.findAPIExceptionHandler;
|
||||
|
||||
@Slf4j
|
||||
public class ReactiveCosmosTemplate implements ReactiveCosmosOperations, ApplicationContextAware {
|
||||
private static final String COUNT_VALUE_KEY = "_aggregate";
|
||||
|
||||
private final MappingCosmosConverter mappingCosmosConverter;
|
||||
private final String databaseName;
|
||||
|
||||
private final CosmosClient cosmosClient;
|
||||
private final ResponseDiagnosticsProcessor responseDiagnosticsProcessor;
|
||||
private final boolean isPopulateQueryMetrics;
|
||||
|
||||
private Function<Class<?>, CosmosEntityInformation<?, ?>> entityInfoCreator =
|
||||
Memoizer.memoize(this::getCosmosEntityInformation);
|
||||
|
||||
private final List<String> containerNameCache;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param cosmosDbFactory the cosmosdbfactory
|
||||
* @param mappingCosmosConverter the mappingCosmosConverter
|
||||
* @param dbName database name
|
||||
*/
|
||||
public ReactiveCosmosTemplate(CosmosDbFactory cosmosDbFactory,
|
||||
MappingCosmosConverter mappingCosmosConverter,
|
||||
String dbName) {
|
||||
Assert.notNull(cosmosDbFactory, "CosmosDbFactory must not be null!");
|
||||
Assert.notNull(mappingCosmosConverter, "MappingCosmosConverter must not be null!");
|
||||
|
||||
this.mappingCosmosConverter = mappingCosmosConverter;
|
||||
this.databaseName = dbName;
|
||||
this.containerNameCache = new ArrayList<>();
|
||||
|
||||
this.cosmosClient = cosmosDbFactory.getCosmosClient();
|
||||
this.responseDiagnosticsProcessor = cosmosDbFactory.getConfig().getResponseDiagnosticsProcessor();
|
||||
this.isPopulateQueryMetrics = cosmosDbFactory.getConfig().isPopulateQueryMetrics();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param applicationContext the application context
|
||||
* @throws BeansException the bean exception
|
||||
*/
|
||||
public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
|
||||
// NOTE: When application context instance variable gets introduced, assign it here.
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a container if it doesn't already exist
|
||||
*
|
||||
* @param information the CosmosEntityInformation
|
||||
* @return Mono containing CosmosContainerResponse
|
||||
*/
|
||||
@Override
|
||||
public Mono<CosmosContainerResponse> createCollectionIfNotExists(CosmosEntityInformation information) {
|
||||
return createContainerIfNotExists(information);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a container if it doesn't already exist
|
||||
*
|
||||
* @param information the CosmosEntityInformation
|
||||
* @return Mono containing CosmosContainerResponse
|
||||
*/
|
||||
@Override
|
||||
public Mono<CosmosContainerResponse> createContainerIfNotExists(CosmosEntityInformation information) {
|
||||
|
||||
return cosmosClient
|
||||
.createDatabaseIfNotExists(this.databaseName)
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to create database", throwable))
|
||||
.flatMap(cosmosDatabaseResponse -> {
|
||||
fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
cosmosDatabaseResponse, null);
|
||||
final CosmosContainerProperties cosmosContainerProperties = new CosmosContainerProperties(
|
||||
information.getContainerName(),
|
||||
"/" + information.getPartitionKeyFieldName());
|
||||
cosmosContainerProperties.defaultTimeToLive(information.getTimeToLive());
|
||||
cosmosContainerProperties.indexingPolicy(information.getIndexingPolicy());
|
||||
return cosmosDatabaseResponse
|
||||
.database()
|
||||
.createContainerIfNotExists(cosmosContainerProperties, information.getRequestUnit())
|
||||
.map(cosmosContainerResponse -> {
|
||||
fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
cosmosContainerResponse, null);
|
||||
this.containerNameCache.add(information.getContainerName());
|
||||
return cosmosContainerResponse;
|
||||
})
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to create container", throwable));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all items in a given container
|
||||
*
|
||||
* @param containerName the containerName
|
||||
* @param domainType the domainType
|
||||
* @return Flux with all the found items or error
|
||||
*/
|
||||
@Override
|
||||
public <T> Flux<T> findAll(String containerName, Class<T> domainType) {
|
||||
final DocumentQuery query = new DocumentQuery(Criteria.getInstance(CriteriaType.ALL));
|
||||
|
||||
return find(query, domainType, containerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all items in a given container
|
||||
*
|
||||
* @param domainType the domainType
|
||||
* @return Flux with all the found items or error
|
||||
*/
|
||||
@Override
|
||||
public <T> Flux<T> findAll(Class<T> domainType) {
|
||||
return findAll(domainType.getSimpleName(), domainType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Flux<T> findAll(PartitionKey partitionKey, Class<T> domainType) {
|
||||
Assert.notNull(partitionKey, "partitionKey should not be null");
|
||||
Assert.notNull(domainType, "domainType should not be null");
|
||||
|
||||
final String containerName = getContainerName(domainType);
|
||||
|
||||
final FeedOptions feedOptions = new FeedOptions();
|
||||
feedOptions.partitionKey(partitionKey);
|
||||
feedOptions.populateQueryMetrics(isPopulateQueryMetrics);
|
||||
|
||||
return cosmosClient
|
||||
.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.readAllItems(feedOptions)
|
||||
.flatMap(cosmosItemFeedResponse -> {
|
||||
fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
null, cosmosItemFeedResponse);
|
||||
return Flux.fromIterable(cosmosItemFeedResponse.results());
|
||||
})
|
||||
.map(cosmosItemProperties -> toDomainObject(domainType, cosmosItemProperties))
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to find items", throwable));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find by id
|
||||
*
|
||||
* @param id the id
|
||||
* @param domainType the domainType
|
||||
* @return Mono with the item or error
|
||||
*/
|
||||
@Override
|
||||
public <T> Mono<T> findById(Object id, Class<T> domainType) {
|
||||
Assert.notNull(domainType, "domainType should not be null");
|
||||
return findById(getContainerName(domainType), id, domainType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find by id
|
||||
*
|
||||
* @param containerName the containername
|
||||
* @param id the id
|
||||
* @param domainType the entity class
|
||||
* @return Mono with the item or error
|
||||
*/
|
||||
@Override
|
||||
public <T> Mono<T> findById(String containerName, Object id, Class<T> domainType) {
|
||||
Assert.hasText(containerName, "containerName should not be null, empty or only whitespaces");
|
||||
Assert.notNull(domainType, "domainType should not be null");
|
||||
assertValidId(id);
|
||||
|
||||
final String query = String.format("select * from root where root.id = '%s'", id.toString());
|
||||
final FeedOptions options = new FeedOptions();
|
||||
options.enableCrossPartitionQuery(true);
|
||||
options.populateQueryMetrics(isPopulateQueryMetrics);
|
||||
|
||||
return cosmosClient.getDatabase(databaseName)
|
||||
.getContainer(containerName)
|
||||
.queryItems(query, options)
|
||||
.flatMap(cosmosItemFeedResponse -> {
|
||||
fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
null, cosmosItemFeedResponse);
|
||||
return Mono.justOrEmpty(cosmosItemFeedResponse
|
||||
.results()
|
||||
.stream()
|
||||
.map(cosmosItem -> toDomainObject(domainType, cosmosItem))
|
||||
.findFirst());
|
||||
})
|
||||
.onErrorResume(throwable ->
|
||||
findAPIExceptionHandler("Failed to find item", throwable))
|
||||
.next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find by id
|
||||
*
|
||||
* @param id the id
|
||||
* @param domainType the entity class
|
||||
* @param partitionKey partition Key
|
||||
* @return Mono with the item or error
|
||||
*/
|
||||
@Override
|
||||
public <T> Mono<T> findById(Object id, Class<T> domainType, PartitionKey partitionKey) {
|
||||
Assert.notNull(domainType, "domainType should not be null");
|
||||
assertValidId(id);
|
||||
|
||||
final String containerName = getContainerName(domainType);
|
||||
return cosmosClient.getDatabase(databaseName)
|
||||
.getContainer(containerName)
|
||||
.getItem(id.toString(), partitionKey)
|
||||
.read()
|
||||
.flatMap(cosmosItemResponse -> {
|
||||
fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
cosmosItemResponse, null);
|
||||
return Mono.justOrEmpty(toDomainObject(domainType,
|
||||
cosmosItemResponse.properties()));
|
||||
})
|
||||
.onErrorResume(throwable ->
|
||||
findAPIExceptionHandler("Failed to find item", throwable));
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert
|
||||
*
|
||||
* @param objectToSave the object to save
|
||||
* @param partitionKey the partition key
|
||||
* @return Mono with the item or error
|
||||
*/
|
||||
public <T> Mono<T> insert(T objectToSave, PartitionKey partitionKey) {
|
||||
Assert.notNull(objectToSave, "domainType should not be null");
|
||||
|
||||
return insert(getContainerName(objectToSave.getClass()), objectToSave, partitionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert
|
||||
*
|
||||
* @param objectToSave the object to save
|
||||
* @return Mono with the item or error
|
||||
*/
|
||||
public <T> Mono<T> insert(T objectToSave) {
|
||||
Assert.notNull(objectToSave, "objectToSave should not be null");
|
||||
|
||||
final Class<T> domainType = (Class<T>) objectToSave.getClass();
|
||||
final CosmosItemProperties originalItem = mappingCosmosConverter.writeCosmosItemProperties(objectToSave);
|
||||
return cosmosClient.getDatabase(this.databaseName)
|
||||
.getContainer(getContainerName(objectToSave.getClass()))
|
||||
.createItem(originalItem, new CosmosItemRequestOptions())
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to insert item", throwable))
|
||||
.flatMap(cosmosItemResponse -> {
|
||||
fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
cosmosItemResponse, null);
|
||||
return Mono.just(toDomainObject(domainType, cosmosItemResponse.properties()));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert
|
||||
*
|
||||
* @param containerName the container name
|
||||
* @param objectToSave the object to save
|
||||
* @param partitionKey the partition key
|
||||
* @return Mono with the item or error
|
||||
*/
|
||||
public <T> Mono<T> insert(String containerName, Object objectToSave, PartitionKey partitionKey) {
|
||||
Assert.hasText(containerName, "containerName should not be null, empty or only whitespaces");
|
||||
Assert.notNull(objectToSave, "objectToSave should not be null");
|
||||
|
||||
final Class<T> domainType = (Class<T>) objectToSave.getClass();
|
||||
final CosmosItemProperties originalItem = mappingCosmosConverter.writeCosmosItemProperties(objectToSave);
|
||||
final CosmosItemRequestOptions options = new CosmosItemRequestOptions();
|
||||
if (partitionKey != null) {
|
||||
options.partitionKey(partitionKey);
|
||||
}
|
||||
return cosmosClient.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.createItem(originalItem, options)
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to insert item", throwable))
|
||||
.flatMap(cosmosItemResponse -> {
|
||||
fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
cosmosItemResponse, null);
|
||||
return Mono.just(toDomainObject(domainType, cosmosItemResponse.properties()));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Upsert
|
||||
*
|
||||
* @param object the object to upsert
|
||||
* @param partitionKey the partition key
|
||||
* @return Mono with the item or error
|
||||
*/
|
||||
@Override
|
||||
public <T> Mono<T> upsert(T object, PartitionKey partitionKey) {
|
||||
return upsert(getContainerName(object.getClass()), object, partitionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upsert
|
||||
*
|
||||
* @param containerName the container name
|
||||
* @param object the object to save
|
||||
* @param partitionKey the partition key
|
||||
* @return Mono with the item or error
|
||||
*/
|
||||
@Override
|
||||
public <T> Mono<T> upsert(String containerName, T object, PartitionKey partitionKey) {
|
||||
final Class<T> domainType = (Class<T>) object.getClass();
|
||||
final CosmosItemProperties originalItem = mappingCosmosConverter.writeCosmosItemProperties(object);
|
||||
final CosmosItemRequestOptions options = new CosmosItemRequestOptions();
|
||||
if (partitionKey != null) {
|
||||
options.partitionKey(partitionKey);
|
||||
}
|
||||
|
||||
applyVersioning(object.getClass(), originalItem, options);
|
||||
|
||||
return cosmosClient.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.upsertItem(originalItem, options)
|
||||
.flatMap(cosmosItemResponse -> {
|
||||
fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
cosmosItemResponse, null);
|
||||
return Mono.just(toDomainObject(domainType, cosmosItemResponse.properties()));
|
||||
})
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to upsert item", throwable));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an item by id
|
||||
*
|
||||
* @param containerName the container name
|
||||
* @param id the id
|
||||
* @param partitionKey the partition key
|
||||
* @return void Mono
|
||||
*/
|
||||
@Override
|
||||
public Mono<Void> deleteById(String containerName, Object id, PartitionKey partitionKey) {
|
||||
Assert.hasText(containerName, "container name should not be null, empty or only whitespaces");
|
||||
assertValidId(id);
|
||||
|
||||
if (partitionKey == null) {
|
||||
partitionKey = PartitionKey.None;
|
||||
}
|
||||
|
||||
final CosmosItemRequestOptions options = new CosmosItemRequestOptions();
|
||||
options.partitionKey(partitionKey);
|
||||
return cosmosClient.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.getItem(id.toString(), partitionKey)
|
||||
.delete(options)
|
||||
.doOnNext(cosmosItemResponse ->
|
||||
fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
cosmosItemResponse, null))
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to delete item", throwable))
|
||||
.then();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all items in a container
|
||||
*
|
||||
* @param containerName the container name
|
||||
* @param partitionKeyName the partition key path
|
||||
* @return void Mono
|
||||
*/
|
||||
@Override
|
||||
public Mono<Void> deleteAll(String containerName, String partitionKeyName) {
|
||||
Assert.hasText(containerName, "container name should not be null, empty or only whitespaces");
|
||||
Assert.notNull(partitionKeyName, "partitionKeyName should not be null");
|
||||
|
||||
final Criteria criteria = Criteria.getInstance(CriteriaType.ALL);
|
||||
final DocumentQuery query = new DocumentQuery(criteria);
|
||||
final SqlQuerySpec sqlQuerySpec = new FindQuerySpecGenerator().generateCosmos(query);
|
||||
final FeedOptions options = new FeedOptions();
|
||||
final boolean isCrossPartitionQuery = query.isCrossPartitionQuery(Collections.singletonList(partitionKeyName));
|
||||
options.enableCrossPartitionQuery(isCrossPartitionQuery);
|
||||
options.populateQueryMetrics(isPopulateQueryMetrics);
|
||||
return cosmosClient.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.queryItems(sqlQuerySpec, options)
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to query items", throwable))
|
||||
.flatMap(cosmosItemFeedResponse -> {
|
||||
fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
null, cosmosItemFeedResponse);
|
||||
return Flux.fromIterable(cosmosItemFeedResponse.results());
|
||||
})
|
||||
.flatMap(cosmosItemProperties -> cosmosClient
|
||||
.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.getItem(cosmosItemProperties.id(), cosmosItemProperties.get(partitionKeyName))
|
||||
.delete()
|
||||
.doOnNext(cosmosItemResponse ->
|
||||
fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
cosmosItemResponse, null))
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to delete items", throwable)))
|
||||
.then();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete items matching query
|
||||
*
|
||||
* @param query the document query
|
||||
* @param domainType the entity class
|
||||
* @param containerName the container name
|
||||
* @return Mono
|
||||
*/
|
||||
@Override
|
||||
public <T> Flux<T> delete(DocumentQuery query, Class<T> domainType, String containerName) {
|
||||
Assert.notNull(query, "DocumentQuery should not be null.");
|
||||
Assert.notNull(domainType, "domainType should not be null.");
|
||||
Assert.hasText(containerName, "container name should not be null, empty or only whitespaces");
|
||||
|
||||
final Flux<CosmosItemProperties> results = findItems(query, domainType, containerName);
|
||||
final List<String> partitionKeyName = getPartitionKeyNames(domainType);
|
||||
|
||||
return results.flatMap(d -> deleteItem(d, partitionKeyName, containerName, domainType))
|
||||
.flatMap(cosmosItemProperties -> Mono.just(toDomainObject(domainType, cosmosItemProperties)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find items
|
||||
*
|
||||
* @param query the document query
|
||||
* @param domainType the entity class
|
||||
* @param containerName the container name
|
||||
* @return Flux with found items or error
|
||||
*/
|
||||
@Override
|
||||
public <T> Flux<T> find(DocumentQuery query, Class<T> domainType, String containerName) {
|
||||
return findItems(query, domainType, containerName)
|
||||
.map(cosmosItemProperties -> toDomainObject(domainType, cosmosItemProperties));
|
||||
}
|
||||
|
||||
/**
|
||||
* Exists
|
||||
*
|
||||
* @param query the document query
|
||||
* @param domainType the entity class
|
||||
* @param containerName the container name
|
||||
* @return Mono with a boolean or error
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> exists(DocumentQuery query, Class<?> domainType, String containerName) {
|
||||
return count(query, true, containerName).flatMap(count -> Mono.just(count > 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Exists
|
||||
* @param id the id
|
||||
* @param domainType the entity class
|
||||
* @param containerName the containercontainer nam,e
|
||||
* @return Mono with a boolean or error
|
||||
*/
|
||||
public Mono<Boolean> existsById(Object id, Class<?> domainType, String containerName) {
|
||||
return findById(containerName, id, domainType)
|
||||
.flatMap(o -> Mono.just(o != null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Count
|
||||
*
|
||||
* @param containerName the container name
|
||||
* @return Mono with the count or error
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> count(String containerName) {
|
||||
final DocumentQuery query = new DocumentQuery(Criteria.getInstance(CriteriaType.ALL));
|
||||
return count(query, true, containerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count
|
||||
*
|
||||
* @param query the document query
|
||||
* @param containerName the container name
|
||||
* @return Mono with count or error
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> count(DocumentQuery query, String containerName) {
|
||||
return count(query, true, containerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingCosmosConverter getConverter() {
|
||||
return mappingCosmosConverter;
|
||||
}
|
||||
|
||||
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) {
|
||||
final SqlQuerySpec querySpec = new CountQueryGenerator().generateCosmos(query);
|
||||
final FeedOptions options = new FeedOptions();
|
||||
|
||||
options.enableCrossPartitionQuery(isCrossPartitionQuery);
|
||||
options.populateQueryMetrics(isPopulateQueryMetrics);
|
||||
|
||||
return executeQuery(querySpec, containerName, options)
|
||||
.doOnNext(feedResponse -> fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
null, feedResponse))
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to get count value", throwable))
|
||||
.next()
|
||||
.map(r -> r.results().get(0).getLong(COUNT_VALUE_KEY));
|
||||
}
|
||||
|
||||
private Flux<FeedResponse<CosmosItemProperties>> executeQuery(SqlQuerySpec sqlQuerySpec, String containerName,
|
||||
FeedOptions options) {
|
||||
|
||||
return cosmosClient.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.queryItems(sqlQuerySpec, options)
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to execute query", throwable));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete container with container name
|
||||
*
|
||||
* @param containerName the container name
|
||||
*/
|
||||
@Override
|
||||
public void deleteContainer(@NonNull String containerName) {
|
||||
Assert.hasText(containerName, "containerName should have text.");
|
||||
cosmosClient.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.delete()
|
||||
.doOnNext(cosmosContainerResponse ->
|
||||
fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
cosmosContainerResponse, null))
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to delete container", throwable))
|
||||
.block();
|
||||
this.containerNameCache.remove(containerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param domainType the domain class
|
||||
* @return the container name
|
||||
*/
|
||||
public String getContainerName(Class<?> domainType) {
|
||||
Assert.notNull(domainType, "domainType should not be null");
|
||||
|
||||
return entityInfoCreator.apply(domainType).getContainerName();
|
||||
}
|
||||
|
||||
private Flux<CosmosItemProperties> findItems(@NonNull DocumentQuery query, @NonNull Class<?> domainType,
|
||||
@NonNull String containerName) {
|
||||
final SqlQuerySpec sqlQuerySpec = new FindQuerySpecGenerator().generateCosmos(query);
|
||||
final boolean isCrossPartitionQuery = query.isCrossPartitionQuery(getPartitionKeyNames(domainType));
|
||||
final FeedOptions feedOptions = new FeedOptions();
|
||||
feedOptions.enableCrossPartitionQuery(isCrossPartitionQuery);
|
||||
feedOptions.populateQueryMetrics(isPopulateQueryMetrics);
|
||||
|
||||
return cosmosClient
|
||||
.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.queryItems(sqlQuerySpec, feedOptions)
|
||||
.flatMap(cosmosItemFeedResponse -> {
|
||||
fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
null, cosmosItemFeedResponse);
|
||||
return Flux.fromIterable(cosmosItemFeedResponse.results());
|
||||
})
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to query items", throwable));
|
||||
}
|
||||
|
||||
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<String> getPartitionKeyNames(Class<?> domainType) {
|
||||
final CosmosEntityInformation<?, ?> entityInfo = entityInfoCreator.apply(domainType);
|
||||
|
||||
if (entityInfo.getPartitionKeyFieldName() == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
return Collections.singletonList(entityInfo.getPartitionKeyFieldName());
|
||||
}
|
||||
|
||||
private Mono<CosmosItemProperties> deleteItem(@NonNull CosmosItemProperties cosmosItemProperties,
|
||||
@NonNull List<String> partitionKeyNames,
|
||||
String containerName,
|
||||
@NonNull Class<?> domainType) {
|
||||
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)));
|
||||
}
|
||||
|
||||
final CosmosItemRequestOptions options = new CosmosItemRequestOptions(partitionKey);
|
||||
applyVersioning(domainType, cosmosItemProperties, options);
|
||||
|
||||
return cosmosClient.getDatabase(this.databaseName)
|
||||
.getContainer(containerName)
|
||||
.getItem(cosmosItemProperties.id(), partitionKey)
|
||||
.delete(options)
|
||||
.map(cosmosItemResponse -> {
|
||||
fillAndProcessResponseDiagnostics(responseDiagnosticsProcessor,
|
||||
cosmosItemResponse, null);
|
||||
return cosmosItemProperties;
|
||||
})
|
||||
.onErrorResume(throwable ->
|
||||
exceptionHandler("Failed to delete item", throwable));
|
||||
}
|
||||
|
||||
private <T> T toDomainObject(@NonNull Class<T> domainType, CosmosItemProperties cosmosItemProperties) {
|
||||
return mappingCosmosConverter.read(domainType, cosmosItemProperties);
|
||||
}
|
||||
|
||||
private void applyVersioning(Class<?> domainType,
|
||||
CosmosItemProperties cosmosItemProperties,
|
||||
CosmosItemRequestOptions options) {
|
||||
|
||||
if (entityInfoCreator.apply(domainType).isVersioned()) {
|
||||
final AccessCondition accessCondition = new AccessCondition();
|
||||
accessCondition.type(AccessConditionType.IF_MATCH);
|
||||
accessCondition.condition(cosmosItemProperties.etag());
|
||||
options.accessCondition(accessCondition);
|
||||
}
|
||||
}
|
||||
|
||||
private CosmosEntityInformation<?, ?> getCosmosEntityInformation(Class<?> domainType) {
|
||||
return new CosmosEntityInformation<>(domainType);
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core;
|
||||
|
||||
import com.azure.data.cosmos.CosmosResponseDiagnostics;
|
||||
import com.azure.data.cosmos.FeedResponse;
|
||||
import com.azure.data.cosmos.FeedResponseDiagnostics;
|
||||
import com.azure.data.cosmos.Resource;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class ResponseDiagnostics {
|
||||
|
||||
private CosmosResponseDiagnostics cosmosResponseDiagnostics;
|
||||
private FeedResponseDiagnostics feedResponseDiagnostics;
|
||||
private CosmosResponseStatistics cosmosResponseStatistics;
|
||||
|
||||
public ResponseDiagnostics(CosmosResponseDiagnostics cosmosResponseDiagnostics,
|
||||
FeedResponseDiagnostics feedResponseDiagnostics) {
|
||||
this.cosmosResponseDiagnostics = cosmosResponseDiagnostics;
|
||||
this.feedResponseDiagnostics = feedResponseDiagnostics;
|
||||
}
|
||||
|
||||
public ResponseDiagnostics(CosmosResponseDiagnostics cosmosResponseDiagnostics,
|
||||
FeedResponseDiagnostics feedResponseDiagnostics,
|
||||
CosmosResponseStatistics cosmosResponseStatistics) {
|
||||
this.cosmosResponseDiagnostics = cosmosResponseDiagnostics;
|
||||
this.feedResponseDiagnostics = feedResponseDiagnostics;
|
||||
this.cosmosResponseStatistics = cosmosResponseStatistics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder diagnostics = new StringBuilder();
|
||||
if (cosmosResponseDiagnostics != null) {
|
||||
diagnostics.append("cosmosResponseDiagnostics={")
|
||||
.append(cosmosResponseDiagnostics)
|
||||
.append("}");
|
||||
}
|
||||
if (feedResponseDiagnostics != null) {
|
||||
if (diagnostics.length() != 0) {
|
||||
diagnostics.append(", ");
|
||||
}
|
||||
diagnostics.append("feedResponseDiagnostics={")
|
||||
.append(feedResponseDiagnostics)
|
||||
.append("}");
|
||||
}
|
||||
if (cosmosResponseStatistics != null) {
|
||||
if (diagnostics.length() != 0) {
|
||||
diagnostics.append(", ");
|
||||
}
|
||||
diagnostics.append("cosmosResponseStatistics={")
|
||||
.append(cosmosResponseStatistics)
|
||||
.append("}");
|
||||
}
|
||||
return diagnostics.toString();
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class CosmosResponseStatistics {
|
||||
|
||||
private final double requestCharge;
|
||||
private final String activityId;
|
||||
|
||||
public <T extends Resource> CosmosResponseStatistics(FeedResponse<T> feedResponse) {
|
||||
this.requestCharge = feedResponse.requestCharge();
|
||||
this.activityId = feedResponse.activityId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CosmosResponseStatistics{" +
|
||||
"requestCharge=" + requestCharge +
|
||||
", activityId='" + activityId + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface ResponseDiagnosticsProcessor {
|
||||
|
||||
/**
|
||||
* Gets called after receiving response from CosmosDb.
|
||||
* Response Diagnostics are collected from API responses and
|
||||
* then set in {@link ResponseDiagnostics} object.
|
||||
* <p>
|
||||
* In case of missing diagnostics from CosmosDb, responseDiagnostics will be null.
|
||||
*
|
||||
* @param responseDiagnostics responseDiagnostics object containing CosmosDb response
|
||||
* diagnostics information
|
||||
*/
|
||||
void processResponseDiagnostics(@Nullable ResponseDiagnostics responseDiagnostics);
|
||||
}
|
|
@ -1,181 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core.convert;
|
||||
|
||||
import com.azure.data.cosmos.CosmosItemProperties;
|
||||
import com.azure.data.cosmos.internal.Utils;
|
||||
import com.azure.data.cosmos.internal.query.QueryItem;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.Constants;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.mapping.CosmosPersistentEntity;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.mapping.CosmosPersistentProperty;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.exception.CosmosDBAccessException;
|
||||
import org.json.JSONObject;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.data.convert.EntityConverter;
|
||||
import org.springframework.data.mapping.MappingException;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Date;
|
||||
|
||||
import static com.microsoft.azure.spring.data.cosmosdb.Constants.ISO_8601_COMPATIBLE_DATE_PATTERN;
|
||||
|
||||
public class MappingCosmosConverter
|
||||
implements EntityConverter<CosmosPersistentEntity<?>, CosmosPersistentProperty,
|
||||
Object, CosmosItemProperties>,
|
||||
ApplicationContextAware {
|
||||
|
||||
protected final MappingContext<? extends CosmosPersistentEntity<?>,
|
||||
CosmosPersistentProperty> mappingContext;
|
||||
protected GenericConversionService conversionService;
|
||||
private ApplicationContext applicationContext;
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
public MappingCosmosConverter(
|
||||
MappingContext<? extends CosmosPersistentEntity<?>, CosmosPersistentProperty> mappingContext,
|
||||
@Qualifier(Constants.OBJECTMAPPER_BEAN_NAME) ObjectMapper objectMapper) {
|
||||
this.mappingContext = mappingContext;
|
||||
this.conversionService = new GenericConversionService();
|
||||
this.objectMapper = objectMapper == null ? ObjectMapperFactory.getObjectMapper() :
|
||||
objectMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> R read(Class<R> type, CosmosItemProperties cosmosItemProperties) {
|
||||
if (cosmosItemProperties == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final CosmosPersistentEntity<?> entity = mappingContext.getPersistentEntity(type);
|
||||
Assert.notNull(entity, "Entity is null.");
|
||||
|
||||
return readInternal(entity, type, cosmosItemProperties);
|
||||
}
|
||||
|
||||
private <R> R readInternal(final CosmosPersistentEntity<?> entity, Class<R> type,
|
||||
final CosmosItemProperties cosmosItemProperties) {
|
||||
|
||||
try {
|
||||
final CosmosPersistentProperty idProperty = entity.getIdProperty();
|
||||
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
|
||||
jsonObject.remove(Constants.ID_PROPERTY_NAME);
|
||||
jsonObject.put(idProperty.getName(), idValue);
|
||||
}
|
||||
|
||||
return objectMapper.readValue(jsonObject.toString(), type);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to read the source document " + cosmosItemProperties.toJson()
|
||||
+ " to target type " + type, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void write(Object sourceEntity, CosmosItemProperties document) {
|
||||
throw new UnsupportedOperationException("The feature is not implemented yet");
|
||||
}
|
||||
|
||||
public CosmosItemProperties writeCosmosItemProperties(Object sourceEntity) {
|
||||
if (sourceEntity == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final CosmosPersistentEntity<?> 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 CosmosPersistentProperty idProperty = persistentEntity.getIdProperty();
|
||||
final CosmosItemProperties cosmosItemProperties;
|
||||
|
||||
try {
|
||||
cosmosItemProperties =
|
||||
new CosmosItemProperties(objectMapper.writeValueAsString(sourceEntity));
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new CosmosDBAccessException("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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConversionService getConversionService() {
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
public MappingContext<? extends CosmosPersistentEntity<?>, CosmosPersistentProperty> getMappingContext() {
|
||||
return mappingContext;
|
||||
}
|
||||
|
||||
|
||||
private ConvertingPropertyAccessor getPropertyAccessor(Object entity) {
|
||||
final CosmosPersistentEntity<?> entityInformation =
|
||||
mappingContext.getPersistentEntity(entity.getClass());
|
||||
|
||||
Assert.notNull(entityInformation, "EntityInformation should not be null.");
|
||||
final PersistentPropertyAccessor accessor = entityInformation.getPropertyAccessor(entity);
|
||||
return new ConvertingPropertyAccessor(accessor, conversionService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a property value to the value stored in CosmosDB
|
||||
*
|
||||
* @param fromPropertyValue
|
||||
* @return
|
||||
*/
|
||||
public static Object toCosmosDbValue(Object fromPropertyValue) {
|
||||
if (fromPropertyValue == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// com.microsoft.azure.data.cosmos.JsonSerializable#set(String, T) cannot set values for Date and Enum correctly
|
||||
|
||||
if (fromPropertyValue instanceof Date) {
|
||||
fromPropertyValue = ((Date) fromPropertyValue).getTime();
|
||||
} else
|
||||
if (fromPropertyValue instanceof ZonedDateTime) {
|
||||
fromPropertyValue = ((ZonedDateTime) fromPropertyValue)
|
||||
.format(DateTimeFormatter.ofPattern(ISO_8601_COMPATIBLE_DATE_PATTERN));
|
||||
} else if (fromPropertyValue instanceof Enum) {
|
||||
fromPropertyValue = fromPropertyValue.toString();
|
||||
}
|
||||
|
||||
return fromPropertyValue;
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core.convert;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
|
||||
|
||||
public class ObjectMapperFactory {
|
||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
|
||||
static {
|
||||
OBJECT_MAPPER.registerModule(new ParameterNamesModule())
|
||||
.registerModule(new Jdk8Module())
|
||||
.registerModule(new JavaTimeModule());
|
||||
OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
}
|
||||
|
||||
public static ObjectMapper getObjectMapper() {
|
||||
return OBJECT_MAPPER;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,220 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core.generator;
|
||||
|
||||
import com.azure.data.cosmos.SqlParameterList;
|
||||
import com.azure.data.cosmos.SqlQuerySpec;
|
||||
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.DocumentQuery;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.exception.IllegalQueryException;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.javatuples.Pair;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.microsoft.azure.spring.data.cosmosdb.core.convert.MappingCosmosConverter.toCosmosDbValue;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
public abstract class AbstractQueryGenerator {
|
||||
|
||||
private String generateQueryParameter(@NonNull String subject) {
|
||||
return subject.replaceAll("\\.", "_"); // user.name is not valid sql parameter identifier.
|
||||
}
|
||||
|
||||
private String generateUnaryQuery(@NonNull Criteria criteria) {
|
||||
Assert.isTrue(criteria.getSubjectValues().isEmpty(), "Unary criteria should have no one subject value");
|
||||
Assert.isTrue(CriteriaType.isUnary(criteria.getType()), "Criteria type should be unary operation");
|
||||
final String subject = criteria.getSubject();
|
||||
|
||||
if (CriteriaType.isFunction(criteria.getType())) {
|
||||
return String.format("%s(r.%s)", criteria.getType().getSqlKeyword(), subject);
|
||||
} else {
|
||||
return String.format("r.%s %s", subject, criteria.getType().getSqlKeyword());
|
||||
}
|
||||
}
|
||||
|
||||
private String generateBinaryQuery(@NonNull Criteria criteria, @NonNull List<Pair<String, Object>> parameters) {
|
||||
Assert.isTrue(criteria.getSubjectValues().size() == 1, "Binary criteria should have only one subject value");
|
||||
Assert.isTrue(CriteriaType.isBinary(criteria.getType()), "Criteria type should be binary operation");
|
||||
|
||||
final String subject = criteria.getSubject();
|
||||
final Object subjectValue = toCosmosDbValue(criteria.getSubjectValues().get(0));
|
||||
final String parameter = generateQueryParameter(subject);
|
||||
|
||||
parameters.add(Pair.with(parameter, subjectValue));
|
||||
|
||||
if (CriteriaType.isFunction(criteria.getType())) {
|
||||
return String.format("%s(r.%s, @%s)", criteria.getType().getSqlKeyword(), subject, parameter);
|
||||
} else {
|
||||
return String.format("r.%s %s @%s", subject, criteria.getType().getSqlKeyword(), parameter);
|
||||
}
|
||||
}
|
||||
|
||||
private String generateBetween(@NonNull Criteria criteria, @NonNull List<Pair<String, Object>> parameters) {
|
||||
final String subject = criteria.getSubject();
|
||||
final Object value1 = toCosmosDbValue(criteria.getSubjectValues().get(0));
|
||||
final Object value2 = toCosmosDbValue(criteria.getSubjectValues().get(1));
|
||||
final String subject1 = subject + "start";
|
||||
final String subject2 = subject + "end";
|
||||
final String parameter1 = generateQueryParameter(subject1);
|
||||
final String parameter2 = generateQueryParameter(subject2);
|
||||
final String keyword = criteria.getType().getSqlKeyword();
|
||||
|
||||
parameters.add(Pair.with(parameter1, value1));
|
||||
parameters.add(Pair.with(parameter2, value2));
|
||||
|
||||
return String.format("(r.%s %s @%s AND @%s)", subject, keyword, parameter1, parameter2);
|
||||
}
|
||||
|
||||
private String generateClosedQuery(@NonNull String left, @NonNull String right, CriteriaType type) {
|
||||
Assert.isTrue(CriteriaType.isClosed(type) && CriteriaType.isBinary(type),
|
||||
"Criteria type should be binary and closure operation");
|
||||
|
||||
return String.join(" ", left, type.getSqlKeyword(), right);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private String generateInQuery(Criteria criteria) {
|
||||
Assert.isTrue(criteria.getSubjectValues().size() == 1, "Criteria should have only one subject value");
|
||||
if (!(criteria.getSubjectValues().get(0) instanceof Collection)) {
|
||||
throw new IllegalQueryException("IN keyword requires Collection type in parameters");
|
||||
}
|
||||
final List<String> inRangeValues = new ArrayList<>();
|
||||
final Collection values = (Collection) criteria.getSubjectValues().get(0);
|
||||
|
||||
values.forEach(o -> {
|
||||
if (o instanceof Integer || o instanceof Long) {
|
||||
inRangeValues.add(String.format("%d", o));
|
||||
} else if (o instanceof String) {
|
||||
inRangeValues.add(String.format("'%s'", (String) o));
|
||||
} else if (o instanceof Boolean) {
|
||||
inRangeValues.add(String.format("%b", (Boolean) o));
|
||||
} else {
|
||||
throw new IllegalQueryException("IN keyword Range only support Number and String type.");
|
||||
}
|
||||
});
|
||||
|
||||
final String inRange = String.join(",", inRangeValues);
|
||||
return String.format("r.%s %s (%s)", criteria.getSubject(), criteria.getType().getSqlKeyword(), inRange);
|
||||
}
|
||||
|
||||
private String generateQueryBody(@NonNull Criteria criteria, @NonNull List<Pair<String, Object>> parameters) {
|
||||
final CriteriaType type = criteria.getType();
|
||||
|
||||
switch (type) {
|
||||
case ALL:
|
||||
return "";
|
||||
case IN:
|
||||
case NOT_IN:
|
||||
return generateInQuery(criteria);
|
||||
case BETWEEN:
|
||||
return generateBetween(criteria, parameters);
|
||||
case IS_NULL:
|
||||
case IS_NOT_NULL:
|
||||
case FALSE:
|
||||
case TRUE:
|
||||
return generateUnaryQuery(criteria);
|
||||
case IS_EQUAL:
|
||||
case NOT:
|
||||
case BEFORE:
|
||||
case AFTER:
|
||||
case LESS_THAN:
|
||||
case LESS_THAN_EQUAL:
|
||||
case GREATER_THAN:
|
||||
case GREATER_THAN_EQUAL:
|
||||
case CONTAINING:
|
||||
case ENDS_WITH:
|
||||
case STARTS_WITH:
|
||||
return generateBinaryQuery(criteria, parameters);
|
||||
case AND:
|
||||
case OR:
|
||||
Assert.isTrue(criteria.getSubCriteria().size() == 2, "criteria should have two SubCriteria");
|
||||
|
||||
final String left = generateQueryBody(criteria.getSubCriteria().get(0), parameters);
|
||||
final String right = generateQueryBody(criteria.getSubCriteria().get(1), parameters);
|
||||
|
||||
return generateClosedQuery(left, right, type);
|
||||
default:
|
||||
throw new UnsupportedOperationException("unsupported Criteria type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a query body for interface QuerySpecGenerator.
|
||||
* The query body compose of Sql query String and its' parameters.
|
||||
* The parameters organized as a list of Pair, for each pair compose parameter name and value.
|
||||
*
|
||||
* @param query the representation for query method.
|
||||
* @return A pair tuple compose of Sql query.
|
||||
*/
|
||||
@NonNull
|
||||
private Pair<String, List<Pair<String, Object>>> generateQueryBody(@NonNull DocumentQuery query) {
|
||||
final List<Pair<String, Object>> parameters = new ArrayList<>();
|
||||
String queryString = this.generateQueryBody(query.getCriteria(), parameters);
|
||||
|
||||
if (StringUtils.hasText(queryString)) {
|
||||
queryString = String.join(" ", "WHERE", queryString);
|
||||
}
|
||||
|
||||
return Pair.with(queryString, parameters);
|
||||
}
|
||||
|
||||
private String getParameter(@NonNull Sort.Order order) {
|
||||
Assert.isTrue(!order.isIgnoreCase(), "Ignore case is not supported");
|
||||
|
||||
final String direction = order.isDescending() ? "DESC" : "ASC";
|
||||
|
||||
return String.format("r.%s %s", order.getProperty(), direction);
|
||||
}
|
||||
|
||||
private String generateQuerySort(@NonNull Sort sort) {
|
||||
if (sort.isUnsorted()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
final String queryTail = "ORDER BY";
|
||||
final List<String> subjects = sort.stream().map(this::getParameter).collect(Collectors.toList());
|
||||
|
||||
return queryTail + " " + String.join(",", subjects);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private String generateQueryTail(@NonNull DocumentQuery query) {
|
||||
final List<String> queryTails = new ArrayList<>();
|
||||
|
||||
queryTails.add(generateQuerySort(query.getSort()));
|
||||
|
||||
return String.join(" ", queryTails.stream().filter(StringUtils::hasText).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
|
||||
protected SqlQuerySpec generateCosmosQuery(@NonNull DocumentQuery query,
|
||||
@NonNull String queryHead) {
|
||||
final Pair<String, List<Pair<String, Object>>> queryBody = generateQueryBody(query);
|
||||
final String queryString = String.join(" ", queryHead, queryBody.getValue0(), generateQueryTail(query));
|
||||
final List<Pair<String, Object>> parameters = queryBody.getValue1();
|
||||
final SqlParameterList sqlParameters =
|
||||
new SqlParameterList();
|
||||
|
||||
sqlParameters.addAll(
|
||||
parameters.stream()
|
||||
.map(p -> new com.azure.data.cosmos.SqlParameter("@" + p.getValue0(),
|
||||
toCosmosDbValue(p.getValue1())))
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
|
||||
return new SqlQuerySpec(queryString, sqlParameters);
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core.generator;
|
||||
|
||||
import com.azure.data.cosmos.SqlQuerySpec;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.query.DocumentQuery;
|
||||
|
||||
public class CountQueryGenerator extends AbstractQueryGenerator implements QuerySpecGenerator {
|
||||
|
||||
@Override
|
||||
public SqlQuerySpec generateCosmos(DocumentQuery query) {
|
||||
return super.generateCosmosQuery(query, "SELECT VALUE COUNT(1) FROM r");
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core.generator;
|
||||
|
||||
import com.azure.data.cosmos.SqlQuerySpec;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.query.DocumentQuery;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@NoArgsConstructor
|
||||
public class FindQuerySpecGenerator extends AbstractQueryGenerator implements QuerySpecGenerator {
|
||||
|
||||
@Override
|
||||
public SqlQuerySpec generateCosmos(DocumentQuery query) {
|
||||
return super.generateCosmosQuery(query, "SELECT * FROM ROOT r");
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core.generator;
|
||||
|
||||
import com.azure.data.cosmos.SqlQuerySpec;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.query.DocumentQuery;
|
||||
|
||||
public interface QuerySpecGenerator {
|
||||
|
||||
/**
|
||||
* Generate the SqlQuerySpec for cosmosDB client.
|
||||
* @param query tree structured query condition.
|
||||
* @return SqlQuerySpec executed by cosmos client.
|
||||
*/
|
||||
SqlQuerySpec generateCosmos(DocumentQuery query);
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core.mapping;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.expression.BeanFactoryAccessor;
|
||||
import org.springframework.context.expression.BeanFactoryResolver;
|
||||
import org.springframework.data.mapping.model.BasicPersistentEntity;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
|
||||
|
||||
public class BasicCosmosPersistentEntity<T> extends BasicPersistentEntity<T, CosmosPersistentProperty>
|
||||
implements CosmosPersistentEntity<T>, ApplicationContextAware {
|
||||
|
||||
private final StandardEvaluationContext context;
|
||||
|
||||
public BasicCosmosPersistentEntity(TypeInformation<T> typeInformation) {
|
||||
super(typeInformation);
|
||||
this.context = new StandardEvaluationContext();
|
||||
}
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
context.addPropertyAccessor(new BeanFactoryAccessor());
|
||||
context.setBeanResolver(new BeanFactoryResolver(applicationContext));
|
||||
context.setRootObject(applicationContext);
|
||||
}
|
||||
|
||||
public String getCollection() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContainer() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguage() {
|
||||
return "";
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core.mapping;
|
||||
|
||||
import com.microsoft.azure.spring.data.cosmosdb.Constants;
|
||||
import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
|
||||
import org.springframework.data.mapping.model.Property;
|
||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
|
||||
|
||||
public class BasicCosmosPersistentProperty extends AnnotationBasedPersistentProperty<CosmosPersistentProperty>
|
||||
implements CosmosPersistentProperty {
|
||||
|
||||
public BasicCosmosPersistentProperty(Property property, CosmosPersistentEntity<?> owner,
|
||||
SimpleTypeHolder simpleTypeHolder) {
|
||||
super(property, owner, simpleTypeHolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Association<CosmosPersistentProperty> createAssociation() {
|
||||
return new Association<>(this, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIdProperty() {
|
||||
|
||||
if (super.isIdProperty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return getName().equals(Constants.ID_PROPERTY_NAME);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core.mapping;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.data.mapping.context.AbstractMappingContext;
|
||||
import org.springframework.data.mapping.model.Property;
|
||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
|
||||
public class CosmosMappingContext
|
||||
extends AbstractMappingContext<BasicCosmosPersistentEntity<?>, CosmosPersistentProperty> {
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
@Override
|
||||
protected <T> BasicCosmosPersistentEntity<T> createPersistentEntity(TypeInformation<T> typeInformation) {
|
||||
final BasicCosmosPersistentEntity<T> entity = new BasicCosmosPersistentEntity<>(typeInformation);
|
||||
|
||||
if (context != null) {
|
||||
entity.setApplicationContext(context);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CosmosPersistentProperty createPersistentProperty(Property property,
|
||||
BasicCosmosPersistentEntity<?> owner,
|
||||
SimpleTypeHolder simpleTypeHolder) {
|
||||
return new BasicCosmosPersistentProperty(property, owner, simpleTypeHolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.context = applicationContext;
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core.mapping;
|
||||
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
|
||||
|
||||
public interface CosmosPersistentEntity<T> extends PersistentEntity<T, CosmosPersistentProperty> {
|
||||
|
||||
@Deprecated
|
||||
String getCollection();
|
||||
|
||||
String getContainer();
|
||||
|
||||
String getLanguage();
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core.mapping;
|
||||
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
|
||||
|
||||
public interface CosmosPersistentProperty extends PersistentProperty<CosmosPersistentProperty> {
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core.mapping;
|
||||
|
||||
import com.microsoft.azure.spring.data.cosmosdb.Constants;
|
||||
import org.springframework.data.annotation.Persistent;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Persistent
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
public @interface Document {
|
||||
|
||||
String collection() default Constants.DEFAULT_COLLECTION_NAME;
|
||||
|
||||
String ru() default Constants.DEFAULT_REQUEST_UNIT;
|
||||
|
||||
int timeToLive() default Constants.DEFAULT_TIME_TO_LIVE;
|
||||
|
||||
boolean autoCreateCollection() default Constants.DEFAULT_AUTO_CREATE_CONTAINER;
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core.mapping;
|
||||
|
||||
import com.azure.data.cosmos.IndexingMode;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.Constants;
|
||||
import org.springframework.data.annotation.Persistent;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Persistent
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface DocumentIndexingPolicy {
|
||||
boolean automatic() default Constants.DEFAULT_INDEXINGPOLICY_AUTOMATIC;
|
||||
|
||||
IndexingMode mode() default IndexingMode.CONSISTENT; // Enum is not really compile time constant
|
||||
|
||||
String[] includePaths() default {};
|
||||
|
||||
String[] excludePaths() default {};
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core.mapping;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD})
|
||||
public @interface PartitionKey {
|
||||
|
||||
/**
|
||||
* The name of the partition key if the serialized attribute name differs from the field name
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String value() default "";
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core.query;
|
||||
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class CosmosPageImpl<T> extends PageImpl<T> {
|
||||
|
||||
private static final long serialVersionUID = 5294396337522314504L;
|
||||
|
||||
// For any query, CosmosDB returns documents less than or equal to page size
|
||||
// Depending on the number of RUs, the number of returned documents can change
|
||||
// Storing the offset of current page, helps to check hasNext and next values
|
||||
private long offset;
|
||||
|
||||
public CosmosPageImpl(List<T> content, Pageable pageable, long total) {
|
||||
super(content, pageable, total);
|
||||
this.offset = pageable.getOffset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTotalPages() {
|
||||
return super.getTotalPages();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalElements() {
|
||||
return super.getTotalElements();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.offset + getContent().size() < getTotalElements();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLast() {
|
||||
return super.isLast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
if (!super.equals(o)) {
|
||||
return false;
|
||||
}
|
||||
final CosmosPageImpl<?> that = (CosmosPageImpl<?>) o;
|
||||
return offset == that.offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), offset);
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core.query;
|
||||
|
||||
import com.azure.data.cosmos.FeedResponse;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
|
||||
/**
|
||||
* CosmosPageRequest representing page request during pagination query, field
|
||||
* {@link FeedResponse#continuationToken()} response continuation token} is saved
|
||||
* to help query next page.
|
||||
* <p>
|
||||
* The requestContinuation token should be saved after each request and reused in later queries.
|
||||
*/
|
||||
public class CosmosPageRequest extends PageRequest {
|
||||
private static final long serialVersionUID = 6093304300037688375L;
|
||||
|
||||
private long offset;
|
||||
|
||||
// Request continuation token used to resume query
|
||||
private String requestContinuation;
|
||||
|
||||
public CosmosPageRequest(int page, int size, String requestContinuation) {
|
||||
super(page, size, Sort.unsorted());
|
||||
this.requestContinuation = requestContinuation;
|
||||
}
|
||||
|
||||
public CosmosPageRequest(int page, int size, String requestContinuation, Sort sort) {
|
||||
super(page, size, sort);
|
||||
this.requestContinuation = requestContinuation;
|
||||
}
|
||||
|
||||
private CosmosPageRequest(long offset, int page, int size, String requestContinuation) {
|
||||
super(page, size, Sort.unsorted());
|
||||
this.offset = offset;
|
||||
this.requestContinuation = requestContinuation;
|
||||
}
|
||||
|
||||
private CosmosPageRequest(long offset, int page, int size, String requestContinuation,
|
||||
Sort sort) {
|
||||
super(page, size, sort);
|
||||
this.offset = offset;
|
||||
this.requestContinuation = requestContinuation;
|
||||
}
|
||||
|
||||
public static CosmosPageRequest of(int page, int size, String requestContinuation, Sort sort) {
|
||||
return new CosmosPageRequest(0, page, size, requestContinuation, sort);
|
||||
}
|
||||
|
||||
public static CosmosPageRequest of(long offset, int page, int size, String requestContinuation, Sort sort) {
|
||||
return new CosmosPageRequest(offset, page, size, requestContinuation, sort);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pageable next() {
|
||||
return new CosmosPageRequest(this.offset + (long) this.getPageSize(),
|
||||
this.getPageNumber() + 1, getPageSize(), this.requestContinuation, getSort());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public String getRequestContinuation() {
|
||||
return this.requestContinuation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
|
||||
result = 31 * result + (requestContinuation != null ? requestContinuation.hashCode() : 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof CosmosPageRequest)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final CosmosPageRequest that = (CosmosPageRequest) obj;
|
||||
|
||||
final boolean continuationTokenEquals = requestContinuation != null ?
|
||||
requestContinuation.equals(that.requestContinuation) : that.requestContinuation == null;
|
||||
|
||||
return continuationTokenEquals && super.equals(that);
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core.query;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
public class Criteria {
|
||||
|
||||
private String subject;
|
||||
private List<Object> subjectValues;
|
||||
private final CriteriaType type;
|
||||
private final List<Criteria> subCriteria;
|
||||
|
||||
private Criteria(CriteriaType type) {
|
||||
this.type = type;
|
||||
this.subCriteria = new ArrayList<>();
|
||||
}
|
||||
|
||||
public static Criteria getInstance(CriteriaType type, @NonNull String subject, @NonNull List<Object> values) {
|
||||
final Criteria criteria = new Criteria(type);
|
||||
|
||||
criteria.subject = subject;
|
||||
criteria.subjectValues = values;
|
||||
|
||||
return criteria;
|
||||
}
|
||||
|
||||
public static Criteria getInstance(CriteriaType type, @NonNull Criteria left, @NonNull Criteria right) {
|
||||
final Criteria criteria = new Criteria(type);
|
||||
|
||||
criteria.subCriteria.add(left);
|
||||
criteria.subCriteria.add(right);
|
||||
|
||||
return criteria;
|
||||
}
|
||||
|
||||
public static Criteria getInstance(CriteriaType type) {
|
||||
return new Criteria(type);
|
||||
}
|
||||
}
|
|
@ -1,185 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core.query;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@AllArgsConstructor
|
||||
public enum CriteriaType {
|
||||
|
||||
ALL(""),
|
||||
IS_EQUAL("="),
|
||||
OR("OR"),
|
||||
AND("AND"),
|
||||
NOT("<>"),
|
||||
BEFORE("<"),
|
||||
AFTER(">"),
|
||||
IN("IN"),
|
||||
NOT_IN("NOT IN"),
|
||||
IS_NULL("IS_NULL"),
|
||||
IS_NOT_NULL("NOT IS_NULL"),
|
||||
LESS_THAN("<"),
|
||||
LESS_THAN_EQUAL("<="),
|
||||
GREATER_THAN(">"),
|
||||
GREATER_THAN_EQUAL(">="),
|
||||
CONTAINING("CONTAINS"),
|
||||
ENDS_WITH("ENDSWITH"),
|
||||
STARTS_WITH("STARTSWITH"),
|
||||
TRUE("= true"),
|
||||
FALSE("= false"),
|
||||
BETWEEN("BETWEEN");
|
||||
|
||||
@Getter
|
||||
private String sqlKeyword;
|
||||
|
||||
private static final Map<Part.Type, CriteriaType> PART_TREE_TYPE_TO_CRITERIA;
|
||||
|
||||
static {
|
||||
final Map<Part.Type, CriteriaType> map = new HashMap<>();
|
||||
|
||||
map.put(Part.Type.NEGATING_SIMPLE_PROPERTY, CriteriaType.NOT);
|
||||
map.put(Part.Type.IS_NULL, CriteriaType.IS_NULL);
|
||||
map.put(Part.Type.IS_NOT_NULL, CriteriaType.IS_NOT_NULL);
|
||||
map.put(Part.Type.SIMPLE_PROPERTY, CriteriaType.IS_EQUAL);
|
||||
map.put(Part.Type.BEFORE, CriteriaType.BEFORE);
|
||||
map.put(Part.Type.AFTER, CriteriaType.AFTER);
|
||||
map.put(Part.Type.IN, CriteriaType.IN);
|
||||
map.put(Part.Type.NOT_IN, CriteriaType.NOT_IN);
|
||||
map.put(Part.Type.GREATER_THAN, CriteriaType.GREATER_THAN);
|
||||
map.put(Part.Type.CONTAINING, CriteriaType.CONTAINING);
|
||||
map.put(Part.Type.ENDING_WITH, CriteriaType.ENDS_WITH);
|
||||
map.put(Part.Type.STARTING_WITH, CriteriaType.STARTS_WITH);
|
||||
map.put(Part.Type.GREATER_THAN_EQUAL, CriteriaType.GREATER_THAN_EQUAL);
|
||||
map.put(Part.Type.LESS_THAN, CriteriaType.LESS_THAN);
|
||||
map.put(Part.Type.LESS_THAN_EQUAL, CriteriaType.LESS_THAN_EQUAL);
|
||||
map.put(Part.Type.TRUE, CriteriaType.TRUE);
|
||||
map.put(Part.Type.FALSE, CriteriaType.FALSE);
|
||||
map.put(Part.Type.BETWEEN, CriteriaType.BETWEEN);
|
||||
|
||||
PART_TREE_TYPE_TO_CRITERIA = Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if PartType is NOT supported.
|
||||
*
|
||||
* @param partType
|
||||
* @return True if unsupported, or false.
|
||||
*/
|
||||
public static boolean isPartTypeUnSupported(@NonNull Part.Type partType) {
|
||||
return !isPartTypeSupported(partType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if PartType is supported.
|
||||
*
|
||||
* @param partType
|
||||
* @return True if supported, or false.
|
||||
*/
|
||||
public static boolean isPartTypeSupported(@NonNull Part.Type partType) {
|
||||
return PART_TREE_TYPE_TO_CRITERIA.containsKey(partType);
|
||||
}
|
||||
|
||||
public static CriteriaType toCriteriaType(@NonNull Part.Type partType) {
|
||||
final CriteriaType criteriaType = PART_TREE_TYPE_TO_CRITERIA.get(partType);
|
||||
|
||||
if (criteriaType == null) {
|
||||
throw new UnsupportedOperationException("Unsupported part type: " + partType);
|
||||
}
|
||||
|
||||
return criteriaType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if CriteriaType operation is closure, with format of (A ops A -> A).
|
||||
* Example: AND, OR.
|
||||
*
|
||||
* @param type
|
||||
* @return True if match, or false.
|
||||
*/
|
||||
public static boolean isClosed(CriteriaType type) {
|
||||
switch (type) {
|
||||
case AND:
|
||||
case OR:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if CriteriaType operation is binary, with format of (A ops A -> B).
|
||||
* Example: IS_EQUAL, AFTER.
|
||||
*
|
||||
* @param type
|
||||
* @return True if match, or false.
|
||||
*/
|
||||
public static boolean isBinary(CriteriaType type) {
|
||||
switch (type) {
|
||||
case IN:
|
||||
case NOT_IN:
|
||||
case AND:
|
||||
case OR:
|
||||
case NOT:
|
||||
case IS_EQUAL:
|
||||
case BEFORE:
|
||||
case AFTER:
|
||||
case LESS_THAN:
|
||||
case LESS_THAN_EQUAL:
|
||||
case GREATER_THAN:
|
||||
case GREATER_THAN_EQUAL:
|
||||
case CONTAINING:
|
||||
case ENDS_WITH:
|
||||
case STARTS_WITH:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if CriteriaType operation is a function.
|
||||
*
|
||||
* @param type
|
||||
* @return True if match, or false.
|
||||
*/
|
||||
public static boolean isFunction(CriteriaType type) {
|
||||
switch (type) {
|
||||
case CONTAINING:
|
||||
case ENDS_WITH:
|
||||
case STARTS_WITH:
|
||||
case IS_NULL:
|
||||
case IS_NOT_NULL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if CriteriaType operation is unary, with format of (ops A -> B).
|
||||
*
|
||||
* @param type
|
||||
* @return True if match, or false.
|
||||
*/
|
||||
public static boolean isUnary(CriteriaType type) {
|
||||
switch (type) {
|
||||
case IS_NULL:
|
||||
case IS_NOT_NULL:
|
||||
case TRUE:
|
||||
case FALSE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.core.query;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class DocumentQuery {
|
||||
|
||||
@Getter
|
||||
private final Criteria criteria;
|
||||
|
||||
@Getter
|
||||
private Sort sort = Sort.unsorted();
|
||||
|
||||
@Getter
|
||||
private Pageable pageable = Pageable.unpaged();
|
||||
|
||||
public DocumentQuery(@NonNull Criteria criteria) {
|
||||
this.criteria = criteria;
|
||||
}
|
||||
|
||||
public DocumentQuery with(@NonNull Sort sort) {
|
||||
if (sort.isSorted()) {
|
||||
this.sort = sort.and(this.sort);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public DocumentQuery with(@NonNull Pageable pageable) {
|
||||
Assert.notNull(pageable, "pageable should not be null");
|
||||
|
||||
this.pageable = pageable;
|
||||
return this;
|
||||
}
|
||||
|
||||
private boolean isCrossPartitionQuery(@NonNull String keyName) {
|
||||
Assert.hasText(keyName, "PartitionKey should have text.");
|
||||
|
||||
final Optional<Criteria> criteria = this.getSubjectCriteria(this.criteria, keyName);
|
||||
|
||||
return criteria.map(criteria1 -> criteria1.getType() != CriteriaType.IS_EQUAL).orElse(true);
|
||||
}
|
||||
|
||||
private boolean hasKeywordOr() {
|
||||
// If there is OR keyword in DocumentQuery, the top node of Criteria must be OR type.
|
||||
return this.criteria.getType() == CriteriaType.OR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate if DocumentQuery should enable cross partition query.
|
||||
*
|
||||
* @param partitionKeys The list of partitionKey names.
|
||||
* @return
|
||||
*/
|
||||
public boolean isCrossPartitionQuery(@NonNull List<String> partitionKeys) {
|
||||
if (partitionKeys.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return partitionKeys.stream().filter(this::isCrossPartitionQuery)
|
||||
.findFirst()
|
||||
.map(p -> true)
|
||||
.orElse(hasKeywordOr());
|
||||
}
|
||||
|
||||
public Optional<Criteria> getCriteriaByType(@NonNull CriteriaType criteriaType) {
|
||||
return getCriteriaByType(criteriaType, this.criteria);
|
||||
}
|
||||
|
||||
private Optional<Criteria> getCriteriaByType(@NonNull CriteriaType criteriaType, @NonNull Criteria criteria) {
|
||||
if (criteria.getType().equals(criteriaType)) {
|
||||
return Optional.of(criteria);
|
||||
}
|
||||
|
||||
for (final Criteria subCriteria: criteria.getSubCriteria()) {
|
||||
if (getCriteriaByType(criteriaType, subCriteria).isPresent()) {
|
||||
return Optional.of(subCriteria);
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private Optional<Criteria> getSubjectCriteria(@NonNull Criteria criteria, @NonNull String keyName) {
|
||||
if (keyName.equals(criteria.getSubject())) {
|
||||
return Optional.of(criteria);
|
||||
}
|
||||
|
||||
final List<Criteria> subCriteriaList = criteria.getSubCriteria();
|
||||
|
||||
for (final Criteria c : subCriteriaList) {
|
||||
final Optional<Criteria> subjectCriteria = getSubjectCriteria(c, keyName);
|
||||
|
||||
if (subjectCriteria.isPresent()) {
|
||||
return subjectCriteria;
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.exception;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
|
||||
public class ConfigurationException extends DataAccessException {
|
||||
|
||||
public ConfigurationException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public ConfigurationException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.exception;
|
||||
|
||||
import com.azure.data.cosmos.CosmosClientException;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Public class extending DataAccessException, exposes innerException.
|
||||
* Every API in {@link com.microsoft.azure.spring.data.cosmosdb.repository.CosmosRepository}
|
||||
* and {@link com.microsoft.azure.spring.data.cosmosdb.repository.ReactiveCosmosRepository}
|
||||
* should throw {@link CosmosDBAccessException}.
|
||||
* innerException refers to the exception thrown by CosmosDB SDK. Callers of repository APIs can
|
||||
* rely on innerException for any retriable logic, or for more details on the failure of
|
||||
* the operation.
|
||||
*/
|
||||
public class CosmosDBAccessException extends DataAccessException {
|
||||
|
||||
protected final CosmosClientException cosmosClientException;
|
||||
|
||||
public CosmosDBAccessException(String msg) {
|
||||
super(msg);
|
||||
this.cosmosClientException = null;
|
||||
}
|
||||
|
||||
public CosmosDBAccessException(@Nullable String msg, @Nullable Throwable cause) {
|
||||
super(msg, cause);
|
||||
if (cause instanceof CosmosClientException) {
|
||||
this.cosmosClientException = (CosmosClientException) cause;
|
||||
} else {
|
||||
this.cosmosClientException = null;
|
||||
}
|
||||
}
|
||||
|
||||
public CosmosDBAccessException(@Nullable String msg, @Nullable Exception cause) {
|
||||
super(msg, cause);
|
||||
this.cosmosClientException = cause instanceof CosmosClientException
|
||||
? (CosmosClientException) cause
|
||||
: null;
|
||||
}
|
||||
|
||||
public CosmosClientException getCosmosClientException() {
|
||||
return cosmosClientException;
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.exception;
|
||||
|
||||
import com.azure.data.cosmos.CosmosClientException;
|
||||
import com.azure.data.cosmos.internal.HttpConstants;
|
||||
import org.springframework.util.StringUtils;
|
||||
import reactor.core.Exceptions;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public class CosmosDBExceptionUtils {
|
||||
|
||||
public static <T> Mono<T> exceptionHandler(String message, Throwable throwable) {
|
||||
if (StringUtils.isEmpty(message)) {
|
||||
message = "Failed to access cosmosdb database";
|
||||
}
|
||||
// Unwrap the exception in case if it is a reactive exception
|
||||
final Throwable unwrappedThrowable = Exceptions.unwrap(throwable);
|
||||
throw new CosmosDBAccessException(message, unwrappedThrowable);
|
||||
}
|
||||
|
||||
public static <T> Mono<T> findAPIExceptionHandler(String message, Throwable throwable) {
|
||||
// Unwrap the exception in case if it is a reactive exception
|
||||
final Throwable unwrappedThrowable = Exceptions.unwrap(throwable);
|
||||
if (unwrappedThrowable instanceof CosmosClientException) {
|
||||
final CosmosClientException cosmosClientException = (CosmosClientException) unwrappedThrowable;
|
||||
if (cosmosClientException.statusCode() == HttpConstants.StatusCodes.NOTFOUND) {
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
return exceptionHandler(message, unwrappedThrowable);
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.exception;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
public class DatabaseCreationException extends DataAccessException {
|
||||
public DatabaseCreationException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public DatabaseCreationException(@Nullable String msg, @Nullable Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.exception;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
public class IllegalCollectionException extends DataAccessException {
|
||||
public IllegalCollectionException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public IllegalCollectionException(@Nullable String msg, @Nullable Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.exception;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
public class IllegalQueryException extends DataAccessException {
|
||||
public IllegalQueryException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public IllegalQueryException(@Nullable String msg, @Nullable Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.repository;
|
||||
|
||||
import com.azure.data.cosmos.PartitionKey;
|
||||
import org.springframework.data.repository.NoRepositoryBean;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@NoRepositoryBean
|
||||
public interface CosmosRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
|
||||
|
||||
/**
|
||||
* Retrieves an entity by its id.
|
||||
*
|
||||
* @param id must not be {@literal null}.
|
||||
* @param partitionKey partition key value of entity, must not be null.
|
||||
* @return the entity with the given id or {@literal Optional#empty()} if none found
|
||||
* @throws IllegalArgumentException if {@code id} is {@literal null}.
|
||||
*/
|
||||
Optional<T> findById(ID id, PartitionKey partitionKey);
|
||||
|
||||
/**
|
||||
* Deletes an entity by its id and partition key.
|
||||
* @param id must not be {@literal null}.
|
||||
* @param partitionKey partition key value of the entity, must not be null.
|
||||
* @throws IllegalArgumentException in case the given {@code id} is {@literal null}.
|
||||
*/
|
||||
void deleteById(ID id, PartitionKey partitionKey);
|
||||
|
||||
/**
|
||||
* Returns list of items in a specific partition
|
||||
* @param partitionKey partition key value
|
||||
* @return list of items with partition key value
|
||||
*/
|
||||
List<T> findAll(PartitionKey partitionKey);
|
||||
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.repository;
|
||||
|
||||
import com.azure.data.cosmos.PartitionKey;
|
||||
import org.springframework.data.repository.NoRepositoryBean;
|
||||
import org.springframework.data.repository.reactive.ReactiveSortingRepository;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@NoRepositoryBean
|
||||
public interface ReactiveCosmosRepository<T, K> extends ReactiveSortingRepository<T, K> {
|
||||
|
||||
/**
|
||||
* Retrieves an entity by its id and partition key.
|
||||
* @param id must not be {@literal null}.
|
||||
* @param partitionKey partition key value of the entity, must not be null.
|
||||
* @return {@link Mono} emitting the entity with the given id or {@link Mono#empty()} if none found.
|
||||
* @throws IllegalArgumentException in case the given {@code id} is {@literal null}.
|
||||
*/
|
||||
Mono<T> findById(K id, PartitionKey partitionKey);
|
||||
|
||||
/**
|
||||
* Deletes an entity by its id and partition key.
|
||||
* @param id must not be {@literal null}.
|
||||
* @param partitionKey partition key value of the entity, must not be null.
|
||||
* @return {@link Mono} emitting the void Mono.
|
||||
* @throws IllegalArgumentException in case the given {@code id} is {@literal null}.
|
||||
*/
|
||||
Mono<Void> deleteById(K id, PartitionKey partitionKey);
|
||||
|
||||
/**
|
||||
* Returns Flux of items in a specific partition
|
||||
* @param partitionKey partition key value
|
||||
* @return {@link Flux} of items with partition key value
|
||||
*/
|
||||
Flux<T> findAll(PartitionKey partitionKey);
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.repository.config;
|
||||
|
||||
import org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
|
||||
public class CosmosRepositoriesRegistrar extends RepositoryBeanDefinitionRegistrarSupport {
|
||||
|
||||
@Override
|
||||
protected Class<? extends Annotation> getAnnotation() {
|
||||
return EnableCosmosRepositories.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RepositoryConfigurationExtension getExtension() {
|
||||
return new CosmosRepositoryConfigurationExtension();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.repository.config;
|
||||
|
||||
import com.microsoft.azure.spring.data.cosmosdb.Constants;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.mapping.CosmosMappingContext;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.repository.CosmosRepository;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.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.data.repository.config.RepositoryConfigurationExtensionSupport;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationSource;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
|
||||
public class CosmosRepositoryConfigurationExtension extends RepositoryConfigurationExtensionSupport {
|
||||
|
||||
@Override
|
||||
public String getModuleName() {
|
||||
return Constants.COSMOSDB_MODULE_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModulePrefix() {
|
||||
return Constants.COSMOSDB_MODULE_PREFIX;
|
||||
}
|
||||
|
||||
public String getRepositoryFactoryBeanClassName() {
|
||||
return CosmosRepositoryFactoryBean.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Class<?>> getIdentifyingTypes() {
|
||||
return Collections.<Class<?>>singleton(CosmosRepository.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Class<? extends Annotation>> getIdentifyingAnnotations() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
// Overriding this to provide reactive repository support.
|
||||
@Override
|
||||
protected boolean useRepositoryConfiguration(RepositoryMetadata metadata) {
|
||||
// CosmosRepository is the sync repository, and hence returning !isReactiveRepository.
|
||||
// ReactiveCosmosRepository is reactive repository.
|
||||
return !metadata.isReactiveRepository();
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.repository.config;
|
||||
|
||||
import com.microsoft.azure.spring.data.cosmosdb.Constants;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.repository.support.CosmosRepositoryFactoryBean;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.repository.config.DefaultRepositoryBaseClass;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@Import(CosmosRepositoriesRegistrar.class)
|
||||
public @interface EnableCosmosRepositories {
|
||||
|
||||
String[] value() default {};
|
||||
|
||||
String[] basePackages() default {};
|
||||
|
||||
Class<?>[] basePackageClasses() default {};
|
||||
|
||||
Filter[] includeFilters() default {};
|
||||
|
||||
Filter[] excludeFilters() default {};
|
||||
|
||||
String repositoryImplementationPostfix() default Constants.DEFAULT_REPOSITORY_IMPLEMENT_POSTFIX;
|
||||
|
||||
String namedQueriesLocation() default "";
|
||||
|
||||
QueryLookupStrategy.Key queryLookupStrategy() default QueryLookupStrategy.Key.CREATE_IF_NOT_FOUND;
|
||||
|
||||
Class<?> repositoryFactoryBeanClass() default CosmosRepositoryFactoryBean.class;
|
||||
|
||||
Class<?> repositoryBaseClass() default DefaultRepositoryBaseClass.class;
|
||||
|
||||
boolean considerNestedRepositories() default false;
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.repository.config;
|
||||
|
||||
import com.microsoft.azure.spring.data.cosmosdb.Constants;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.repository.support.ReactiveCosmosRepositoryFactoryBean;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.repository.config.DefaultRepositoryBaseClass;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@Import(ReactiveCosmosRepositoriesRegistrar.class)
|
||||
public @interface EnableReactiveCosmosRepositories {
|
||||
|
||||
String[] value() default {};
|
||||
|
||||
String[] basePackages() default {};
|
||||
|
||||
Class<?>[] basePackageClasses() default {};
|
||||
|
||||
Filter[] includeFilters() default {};
|
||||
|
||||
Filter[] excludeFilters() default {};
|
||||
|
||||
String repositoryImplementationPostfix() default Constants.DEFAULT_REPOSITORY_IMPLEMENT_POSTFIX;
|
||||
|
||||
String namedQueriesLocation() default "";
|
||||
|
||||
QueryLookupStrategy.Key queryLookupStrategy() default QueryLookupStrategy.Key.CREATE_IF_NOT_FOUND;
|
||||
|
||||
Class<?> repositoryFactoryBeanClass() default ReactiveCosmosRepositoryFactoryBean.class;
|
||||
|
||||
Class<?> repositoryBaseClass() default DefaultRepositoryBaseClass.class;
|
||||
|
||||
boolean considerNestedRepositories() default false;
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.repository.config;
|
||||
|
||||
import org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
|
||||
public class ReactiveCosmosRepositoriesRegistrar extends RepositoryBeanDefinitionRegistrarSupport {
|
||||
|
||||
@Override
|
||||
protected Class<? extends Annotation> getAnnotation() {
|
||||
return EnableReactiveCosmosRepositories.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RepositoryConfigurationExtension getExtension() {
|
||||
return new ReactiveCosmosRepositoryConfigurationExtension();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.spring.data.cosmosdb.repository.config;
|
||||
|
||||
import com.microsoft.azure.spring.data.cosmosdb.Constants;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.mapping.CosmosMappingContext;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.repository.ReactiveCosmosRepository;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.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.data.repository.config.RepositoryConfigurationExtensionSupport;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationSource;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
|
||||
public class ReactiveCosmosRepositoryConfigurationExtension extends RepositoryConfigurationExtensionSupport {
|
||||
|
||||
@Override
|
||||
public String getModuleName() {
|
||||
return Constants.COSMOSDB_MODULE_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModulePrefix() {
|
||||
return Constants.COSMOSDB_MODULE_PREFIX;
|
||||
}
|
||||
|
||||
public String getRepositoryFactoryBeanClassName() {
|
||||
return ReactiveCosmosRepositoryFactoryBean.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Class<?>> getIdentifyingTypes() {
|
||||
return Collections.<Class<?>>singleton(ReactiveCosmosRepository.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Class<? extends Annotation>> getIdentifyingAnnotations() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
// Overriding this to provide reactive repository support.
|
||||
@Override
|
||||
protected boolean useRepositoryConfiguration(RepositoryMetadata metadata) {
|
||||
return metadata.isReactiveRepository();
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.repository.query;
|
||||
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.CosmosOperations;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.query.DocumentQuery;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
|
||||
public abstract class AbstractCosmosQuery implements RepositoryQuery {
|
||||
|
||||
private final CosmosQueryMethod method;
|
||||
private final CosmosOperations operations;
|
||||
|
||||
public AbstractCosmosQuery(CosmosQueryMethod method, CosmosOperations operations) {
|
||||
this.method = method;
|
||||
this.operations = operations;
|
||||
}
|
||||
|
||||
public Object execute(Object[] parameters) {
|
||||
final CosmosParameterAccessor accessor = new CosmosParameterParameterAccessor(method, parameters);
|
||||
final DocumentQuery query = createQuery(accessor);
|
||||
|
||||
final ResultProcessor processor = method.getResultProcessor().withDynamicProjection(accessor);
|
||||
final String container = ((CosmosEntityMetadata) method.getEntityInformation()).getContainerName();
|
||||
|
||||
final CosmosQueryExecution execution = getExecution(accessor);
|
||||
return execution.execute(query, processor.getReturnedType().getDomainType(), container);
|
||||
}
|
||||
|
||||
|
||||
private CosmosQueryExecution getExecution(CosmosParameterAccessor accessor) {
|
||||
if (isDeleteQuery()) {
|
||||
return new CosmosQueryExecution.DeleteExecution(operations);
|
||||
} else if (method.isPageQuery()) {
|
||||
return new CosmosQueryExecution.PagedExecution(operations, accessor.getPageable());
|
||||
} else if (isExistsQuery()) {
|
||||
return new CosmosQueryExecution.ExistsExecution(operations);
|
||||
} else {
|
||||
return new CosmosQueryExecution.MultiEntityExecution(operations);
|
||||
}
|
||||
}
|
||||
|
||||
public CosmosQueryMethod getQueryMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
protected abstract DocumentQuery createQuery(CosmosParameterAccessor accessor);
|
||||
|
||||
protected abstract boolean isDeleteQuery();
|
||||
|
||||
protected abstract boolean isExistsQuery();
|
||||
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.repository.query;
|
||||
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.ReactiveCosmosOperations;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.query.DocumentQuery;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
|
||||
public abstract class AbstractReactiveCosmosQuery implements RepositoryQuery {
|
||||
|
||||
private final ReactiveCosmosQueryMethod method;
|
||||
private final ReactiveCosmosOperations operations;
|
||||
|
||||
public AbstractReactiveCosmosQuery(ReactiveCosmosQueryMethod method,
|
||||
ReactiveCosmosOperations operations) {
|
||||
this.method = method;
|
||||
this.operations = operations;
|
||||
}
|
||||
|
||||
public Object execute(Object[] parameters) {
|
||||
final ReactiveCosmosParameterAccessor accessor =
|
||||
new ReactiveCosmosParameterParameterAccessor(method, parameters);
|
||||
final DocumentQuery query = createQuery(accessor);
|
||||
|
||||
final ResultProcessor processor =
|
||||
method.getResultProcessor().withDynamicProjection(accessor);
|
||||
final String containerName =
|
||||
((ReactiveCosmosEntityMetadata) method.getEntityInformation()).getContainerName();
|
||||
|
||||
final ReactiveCosmosQueryExecution execution = getExecution(accessor);
|
||||
return execution.execute(query, processor.getReturnedType().getDomainType(), containerName);
|
||||
}
|
||||
|
||||
|
||||
private ReactiveCosmosQueryExecution getExecution(ReactiveCosmosParameterAccessor accessor) {
|
||||
if (isDeleteQuery()) {
|
||||
return new ReactiveCosmosQueryExecution.DeleteExecution(operations);
|
||||
} else if (method.isPageQuery()) {
|
||||
throw new IllegalArgumentException("Paged Query is not supported by reactive cosmos " +
|
||||
"db");
|
||||
} else if (isExistsQuery()) {
|
||||
return new ReactiveCosmosQueryExecution.ExistsExecution(operations);
|
||||
} else {
|
||||
return new ReactiveCosmosQueryExecution.MultiEntityExecution(operations);
|
||||
}
|
||||
}
|
||||
|
||||
public ReactiveCosmosQueryMethod getQueryMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
protected abstract DocumentQuery createQuery(ReactiveCosmosParameterAccessor accessor);
|
||||
|
||||
protected abstract boolean isDeleteQuery();
|
||||
|
||||
protected abstract boolean isExistsQuery();
|
||||
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.repository.query;
|
||||
|
||||
import org.springframework.data.repository.core.EntityMetadata;
|
||||
|
||||
public interface CosmosEntityMetadata<T> extends EntityMetadata<T> {
|
||||
|
||||
/**
|
||||
* Use getContainerName() instead
|
||||
* @return container name
|
||||
*/
|
||||
@Deprecated
|
||||
String getCollectionName();
|
||||
|
||||
String getContainerName();
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.repository.query;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.repository.query.Parameter;
|
||||
|
||||
public class CosmosParameter extends Parameter {
|
||||
|
||||
public CosmosParameter(MethodParameter parameter) {
|
||||
super(parameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSpecialParameter() {
|
||||
return super.isSpecialParameter();
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.repository.query;
|
||||
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
|
||||
public interface CosmosParameterAccessor extends ParameterAccessor {
|
||||
Object[] getValues();
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.repository.query;
|
||||
|
||||
import org.springframework.data.repository.query.ParametersParameterAccessor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class CosmosParameterParameterAccessor extends ParametersParameterAccessor
|
||||
implements CosmosParameterAccessor {
|
||||
|
||||
private final List<Object> values;
|
||||
|
||||
public CosmosParameterParameterAccessor(CosmosQueryMethod method, Object[] values) {
|
||||
super(method.getParameters(), values);
|
||||
|
||||
this.values = Arrays.asList(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getValues() {
|
||||
return values.toArray();
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.repository.query;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
public class CosmosParameters extends Parameters<CosmosParameters, CosmosParameter> {
|
||||
|
||||
public CosmosParameters(Method method) {
|
||||
super(method);
|
||||
}
|
||||
|
||||
private CosmosParameters(List<CosmosParameter> parameters) {
|
||||
super(parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CosmosParameters createFrom(List<CosmosParameter> parameters) {
|
||||
return new CosmosParameters(parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CosmosParameter createParameter(MethodParameter parameter) {
|
||||
return new CosmosParameter(parameter);
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.repository.query;
|
||||
|
||||
import com.microsoft.azure.spring.data.cosmosdb.Constants;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.mapping.CosmosPersistentProperty;
|
||||
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.DocumentQuery;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.repository.support.CosmosEntityInformation;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
// TODO: String based query, based on how cosmosdb provides.
|
||||
// StringCosmosQuery class,
|
||||
// How to bind values to the query. if CosmosDb already has binding capability, if not we would have to do it here in
|
||||
// some creative way.query creator are associated with part tree queries,
|
||||
public class CosmosQueryCreator extends AbstractQueryCreator<DocumentQuery, Criteria> {
|
||||
|
||||
private final MappingContext<?, CosmosPersistentProperty> mappingContext;
|
||||
|
||||
public CosmosQueryCreator(PartTree tree, CosmosParameterAccessor accessor,
|
||||
MappingContext<?, CosmosPersistentProperty> mappingContext) {
|
||||
super(tree, accessor);
|
||||
|
||||
this.mappingContext = mappingContext;
|
||||
}
|
||||
|
||||
private String getSubject(@NonNull Part part) {
|
||||
String subject = mappingContext.getPersistentPropertyPath(part.getProperty()).toDotPath();
|
||||
final Class<?> domainType = part.getProperty().getOwningType().getType();
|
||||
|
||||
@SuppressWarnings("unchecked") final CosmosEntityInformation information =
|
||||
new CosmosEntityInformation(domainType);
|
||||
|
||||
if (information.getIdField().getName().equals(subject)) {
|
||||
subject = Constants.ID_PROPERTY_NAME;
|
||||
}
|
||||
|
||||
return subject;
|
||||
}
|
||||
|
||||
@Override // Note (panli): side effect here, this method will change the iterator status of parameters.
|
||||
protected Criteria create(Part part, Iterator<Object> parameters) {
|
||||
final Part.Type type = part.getType();
|
||||
final String subject = getSubject(part);
|
||||
final List<Object> values = new ArrayList<>();
|
||||
|
||||
if (CriteriaType.isPartTypeUnSupported(type)) {
|
||||
throw new UnsupportedOperationException("Unsupported keyword: " + type);
|
||||
}
|
||||
|
||||
for (int i = 0; i < part.getNumberOfArguments(); i++) {
|
||||
Assert.isTrue(parameters.hasNext(), "should not reach the end of iterator");
|
||||
values.add(parameters.next());
|
||||
}
|
||||
|
||||
return Criteria.getInstance(CriteriaType.toCriteriaType(type), subject, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Criteria and(@NonNull Part part, @NonNull Criteria base, @NonNull Iterator<Object> parameters) {
|
||||
final Criteria right = this.create(part, parameters);
|
||||
|
||||
return Criteria.getInstance(CriteriaType.AND, base, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Criteria or(@NonNull Criteria base, @NonNull Criteria criteria) {
|
||||
return Criteria.getInstance(CriteriaType.OR, base, criteria);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DocumentQuery complete(@NonNull Criteria criteria, @NonNull Sort sort) {
|
||||
return new DocumentQuery(criteria).with(sort);
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.repository.query;
|
||||
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.CosmosOperations;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.query.CosmosPageRequest;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.query.DocumentQuery;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
public interface CosmosQueryExecution {
|
||||
Object execute(DocumentQuery query, Class<?> type, String container);
|
||||
|
||||
final class ContainerExecution implements CosmosQueryExecution {
|
||||
|
||||
private final CosmosOperations operations;
|
||||
|
||||
public ContainerExecution(CosmosOperations operations) {
|
||||
this.operations = operations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(DocumentQuery query, Class<?> type, String container) {
|
||||
return operations.getContainerName(type);
|
||||
}
|
||||
}
|
||||
|
||||
final class MultiEntityExecution implements CosmosQueryExecution {
|
||||
|
||||
private final CosmosOperations operations;
|
||||
|
||||
public MultiEntityExecution(CosmosOperations operations) {
|
||||
this.operations = operations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(DocumentQuery query, Class<?> type, String container) {
|
||||
return operations.find(query, type, container);
|
||||
}
|
||||
}
|
||||
|
||||
final class ExistsExecution implements CosmosQueryExecution {
|
||||
|
||||
private final CosmosOperations operations;
|
||||
|
||||
public ExistsExecution(CosmosOperations operations) {
|
||||
this.operations = operations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(DocumentQuery query, Class<?> type, String container) {
|
||||
return operations.exists(query, type, container);
|
||||
}
|
||||
}
|
||||
|
||||
final class DeleteExecution implements CosmosQueryExecution {
|
||||
|
||||
private final CosmosOperations operations;
|
||||
|
||||
public DeleteExecution(CosmosOperations operations) {
|
||||
this.operations = operations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(DocumentQuery query, Class<?> type, String container) {
|
||||
return operations.delete(query, type, container);
|
||||
}
|
||||
}
|
||||
|
||||
final class PagedExecution implements CosmosQueryExecution {
|
||||
private final CosmosOperations operations;
|
||||
private final Pageable pageable;
|
||||
|
||||
public PagedExecution(CosmosOperations operations, Pageable pageable) {
|
||||
this.operations = operations;
|
||||
this.pageable = pageable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(DocumentQuery query, Class<?> type, String container) {
|
||||
if (pageable.getPageNumber() != 0 && !(pageable instanceof CosmosPageRequest)) {
|
||||
throw new IllegalStateException("Not the first page but Pageable is not a valid " +
|
||||
"CosmosPageRequest, requestContinuation is required for non first page request");
|
||||
}
|
||||
|
||||
query.with(pageable);
|
||||
|
||||
return operations.paginationQuery(query, type, container);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.repository.query;
|
||||
|
||||
import com.microsoft.azure.spring.data.cosmosdb.repository.support.CosmosEntityInformation;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.repository.core.EntityMetadata;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class CosmosQueryMethod extends QueryMethod {
|
||||
|
||||
private CosmosEntityMetadata<?> metadata;
|
||||
|
||||
public CosmosQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory) {
|
||||
super(method, metadata, factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public EntityMetadata<?> getEntityInformation() {
|
||||
final Class<Object> domainType = (Class<Object>) getDomainClass();
|
||||
final CosmosEntityInformation entityInformation =
|
||||
new CosmosEntityInformation<Object, String>(domainType);
|
||||
|
||||
this.metadata = new SimpleCosmosEntityMetadata<Object>(domainType, entityInformation);
|
||||
return this.metadata;
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.repository.query;
|
||||
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.CosmosOperations;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.query.DocumentQuery;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.mapping.CosmosPersistentProperty;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
|
||||
public class PartTreeCosmosQuery extends AbstractCosmosQuery {
|
||||
|
||||
private final PartTree tree;
|
||||
private final MappingContext<?, CosmosPersistentProperty> mappingContext;
|
||||
private final ResultProcessor processor;
|
||||
|
||||
public PartTreeCosmosQuery(CosmosQueryMethod method, CosmosOperations operations) {
|
||||
super(method, operations);
|
||||
|
||||
this.processor = method.getResultProcessor();
|
||||
this.tree = new PartTree(method.getName(), processor.getReturnedType().getDomainType());
|
||||
this.mappingContext = operations.getConverter().getMappingContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DocumentQuery createQuery(CosmosParameterAccessor accessor) {
|
||||
final CosmosQueryCreator creator = new CosmosQueryCreator(tree, accessor, mappingContext);
|
||||
|
||||
final DocumentQuery query = creator.createQuery();
|
||||
|
||||
if (tree.isLimiting()) {
|
||||
throw new NotImplementedException("Limiting is not supported.");
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isDeleteQuery() {
|
||||
return tree.isDelete();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isExistsQuery() {
|
||||
return tree.isExistsProjection();
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.repository.query;
|
||||
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.ReactiveCosmosOperations;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.mapping.CosmosPersistentProperty;
|
||||
import com.microsoft.azure.spring.data.cosmosdb.core.query.DocumentQuery;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
|
||||
public class PartTreeReactiveCosmosQuery extends AbstractReactiveCosmosQuery {
|
||||
|
||||
private final PartTree tree;
|
||||
private final MappingContext<?, CosmosPersistentProperty> mappingContext;
|
||||
private final ResultProcessor processor;
|
||||
|
||||
public PartTreeReactiveCosmosQuery(ReactiveCosmosQueryMethod method, ReactiveCosmosOperations operations) {
|
||||
super(method, operations);
|
||||
|
||||
this.processor = method.getResultProcessor();
|
||||
this.tree = new PartTree(method.getName(), processor.getReturnedType().getDomainType());
|
||||
this.mappingContext = operations.getConverter().getMappingContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DocumentQuery createQuery(ReactiveCosmosParameterAccessor accessor) {
|
||||
final ReactiveCosmosQueryCreator creator = new ReactiveCosmosQueryCreator(tree, accessor, mappingContext);
|
||||
|
||||
final DocumentQuery query = creator.createQuery();
|
||||
|
||||
if (tree.isLimiting()) {
|
||||
throw new NotImplementedException("Limiting is not supported.");
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isDeleteQuery() {
|
||||
return tree.isDelete();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isExistsQuery() {
|
||||
return tree.isExistsProjection();
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.repository.query;
|
||||
|
||||
import org.springframework.data.repository.core.EntityMetadata;
|
||||
|
||||
public interface ReactiveCosmosEntityMetadata<T> extends EntityMetadata {
|
||||
@Deprecated
|
||||
String getCollectionName();
|
||||
|
||||
String getContainerName();
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.cosmosdb.repository.query;
|
||||
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
|
||||
public interface ReactiveCosmosParameterAccessor extends ParameterAccessor {
|
||||
Object[] getValues();
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче