Moved spring-data-cosmosdb repo to new repo

This commit is contained in:
Kushagra Thapar 2020-07-09 09:06:50 -07:00
Родитель f62cf3670b
Коммит 7acfde5db9
217 изменённых файлов: 4 добавлений и 17315 удалений

Просмотреть файл

@ -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'

45
.gitignore поставляемый
Просмотреть файл

@ -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

Просмотреть файл

@ -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
Просмотреть файл

@ -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.

Просмотреть файл

@ -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 }

Просмотреть файл

@ -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>

Просмотреть файл

@ -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

225
mvnw поставляемый
Просмотреть файл

@ -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 "$@"

143
mvnw.cmd поставляемый
Просмотреть файл

@ -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
Просмотреть файл

@ -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>

22
samplecode/.gitignore поставляемый
Просмотреть файл

@ -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();
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше