Skip to content

Commit 3719570

Browse files
Fix query deserialization (Azure#19438)
* rename BookRepositoryIT > AnnotatedQueryIT to better reflect purpose * demonstrate failure to deserialize object with OffsetDateTime property when @query is used * use JsonNdoe as input type to CosmosAsyncContainer#queryItem and use MappingCosmosConverter to convert response to domain type like all other CosmosTemplate methods do * @query currently only supports List results so update test accordingly
1 parent 6309213 commit 3719570

File tree

8 files changed

+122
-12
lines changed

8 files changed

+122
-12
lines changed

sdk/cosmos/azure-spring-data-cosmos-test/src/test/java/com/azure/spring/data/cosmos/core/CosmosTemplateIT.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,26 @@
77
import com.azure.cosmos.CosmosException;
88
import com.azure.cosmos.implementation.ConflictException;
99
import com.azure.cosmos.models.PartitionKey;
10+
import com.azure.cosmos.models.SqlQuerySpec;
1011
import com.azure.spring.data.cosmos.CosmosFactory;
1112
import com.azure.spring.data.cosmos.common.PageTestUtils;
1213
import com.azure.spring.data.cosmos.common.ResponseDiagnosticsTestUtils;
1314
import com.azure.spring.data.cosmos.common.TestConstants;
1415
import com.azure.spring.data.cosmos.common.TestUtils;
1516
import com.azure.spring.data.cosmos.config.CosmosConfig;
1617
import com.azure.spring.data.cosmos.core.convert.MappingCosmosConverter;
18+
import com.azure.spring.data.cosmos.core.generator.FindQuerySpecGenerator;
1719
import com.azure.spring.data.cosmos.core.mapping.CosmosMappingContext;
1820
import com.azure.spring.data.cosmos.core.query.CosmosPageRequest;
1921
import com.azure.spring.data.cosmos.core.query.CosmosQuery;
2022
import com.azure.spring.data.cosmos.core.query.Criteria;
2123
import com.azure.spring.data.cosmos.core.query.CriteriaType;
24+
import com.azure.spring.data.cosmos.domain.AuditableEntity;
2225
import com.azure.spring.data.cosmos.domain.GenIdEntity;
2326
import com.azure.spring.data.cosmos.domain.Person;
2427
import com.azure.spring.data.cosmos.exception.CosmosAccessException;
2528
import com.azure.spring.data.cosmos.repository.TestRepositoryConfig;
29+
import com.azure.spring.data.cosmos.repository.repository.AuditableRepository;
2630
import com.azure.spring.data.cosmos.repository.support.CosmosEntityInformation;
2731
import org.assertj.core.util.Lists;
2832
import org.junit.After;
@@ -64,6 +68,7 @@
6468
import static com.azure.spring.data.cosmos.common.TestConstants.UPDATED_FIRST_NAME;
6569
import static org.assertj.core.api.Assertions.assertThat;
6670
import static org.junit.Assert.assertEquals;
71+
import static org.junit.Assert.assertNotNull;
6772
import static org.junit.Assert.fail;
6873

6974
@RunWith(SpringJUnit4ClassRunner.class)
@@ -99,6 +104,8 @@ public class CosmosTemplateIT {
99104
private CosmosConfig cosmosConfig;
100105
@Autowired
101106
private ResponseDiagnosticsTestUtils responseDiagnosticsTestUtils;
107+
@Autowired
108+
private AuditableRepository auditableRepository;
102109

103110
@Before
104111
public void setUp() throws ClassNotFoundException {
@@ -562,4 +569,31 @@ public void testBetweenCriteria() {
562569
containerName));
563570
assertThat(people).containsExactly(TEST_PERSON);
564571
}
572+
573+
@Test
574+
public void testRunQueryWithSimpleReturnType() {
575+
Criteria ageBetween = Criteria.getInstance(CriteriaType.BETWEEN, "age", Arrays.asList(AGE - 1, AGE + 1),
576+
Part.IgnoreCaseType.NEVER);
577+
final SqlQuerySpec sqlQuerySpec = new FindQuerySpecGenerator().generateCosmos(new CosmosQuery(ageBetween));
578+
List<Person> people = TestUtils.toList(cosmosTemplate.runQuery(sqlQuerySpec, Person.class, Person.class));
579+
assertThat(people).containsExactly(TEST_PERSON);
580+
}
581+
582+
@Test
583+
public void testRunQueryWithReturnTypeContainingLocalDateTime() {
584+
final AuditableEntity entity = new AuditableEntity();
585+
entity.setId(UUID.randomUUID().toString());
586+
587+
auditableRepository.save(entity);
588+
589+
Criteria equals = Criteria.getInstance(CriteriaType.IS_EQUAL, "id", Collections.singletonList(entity.getId()), Part.IgnoreCaseType.NEVER);
590+
final SqlQuerySpec sqlQuerySpec = new FindQuerySpecGenerator().generateCosmos(new CosmosQuery(equals));
591+
List<AuditableEntity> results = TestUtils.toList(cosmosTemplate.runQuery(sqlQuerySpec, AuditableEntity.class, AuditableEntity.class));
592+
assertEquals(results.size(), 1);
593+
AuditableEntity foundEntity = results.get(0);
594+
assertEquals(entity.getId(), foundEntity.getId());
595+
assertNotNull(foundEntity.getCreatedDate());
596+
assertNotNull(foundEntity.getLastModifiedByDate());
597+
}
598+
565599
}

sdk/cosmos/azure-spring-data-cosmos-test/src/test/java/com/azure/spring/data/cosmos/core/ReactiveCosmosTemplateIT.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,24 @@
88
import com.azure.cosmos.CosmosException;
99
import com.azure.cosmos.implementation.ConflictException;
1010
import com.azure.cosmos.models.PartitionKey;
11+
import com.azure.cosmos.models.SqlQuerySpec;
1112
import com.azure.spring.data.cosmos.CosmosFactory;
1213
import com.azure.spring.data.cosmos.common.ResponseDiagnosticsTestUtils;
1314
import com.azure.spring.data.cosmos.common.TestConstants;
15+
import com.azure.spring.data.cosmos.common.TestUtils;
1416
import com.azure.spring.data.cosmos.config.CosmosConfig;
1517
import com.azure.spring.data.cosmos.core.convert.MappingCosmosConverter;
18+
import com.azure.spring.data.cosmos.core.generator.FindQuerySpecGenerator;
1619
import com.azure.spring.data.cosmos.core.mapping.CosmosMappingContext;
1720
import com.azure.spring.data.cosmos.core.query.CosmosQuery;
1821
import com.azure.spring.data.cosmos.core.query.Criteria;
1922
import com.azure.spring.data.cosmos.core.query.CriteriaType;
23+
import com.azure.spring.data.cosmos.domain.AuditableEntity;
2024
import com.azure.spring.data.cosmos.domain.GenIdEntity;
2125
import com.azure.spring.data.cosmos.domain.Person;
2226
import com.azure.spring.data.cosmos.exception.CosmosAccessException;
2327
import com.azure.spring.data.cosmos.repository.TestRepositoryConfig;
28+
import com.azure.spring.data.cosmos.repository.repository.AuditableRepository;
2429
import com.azure.spring.data.cosmos.repository.support.CosmosEntityInformation;
2530
import org.assertj.core.api.Assertions;
2631
import org.junit.After;
@@ -42,7 +47,10 @@
4247
import reactor.test.StepVerifier;
4348

4449
import java.util.ArrayList;
50+
import java.util.Arrays;
4551
import java.util.Collections;
52+
import java.util.List;
53+
import java.util.UUID;
4654
import java.util.function.Predicate;
4755

4856
import static com.azure.spring.data.cosmos.common.TestConstants.ADDRESSES;
@@ -53,6 +61,8 @@
5361
import static org.assertj.core.api.Assertions.assertThat;
5462
import static org.hamcrest.Matchers.equalTo;
5563
import static org.hamcrest.Matchers.is;
64+
import static org.junit.Assert.assertEquals;
65+
import static org.junit.Assert.assertNotNull;
5666
import static org.junit.Assert.fail;
5767

5868
@RunWith(SpringJUnit4ClassRunner.class)
@@ -100,6 +110,8 @@ public class ReactiveCosmosTemplateIT {
100110
private CosmosClientBuilder cosmosClientBuilder;
101111
@Autowired
102112
private ResponseDiagnosticsTestUtils responseDiagnosticsTestUtils;
113+
@Autowired
114+
private AuditableRepository auditableRepository;
103115

104116
@Before
105117
public void setUp() throws ClassNotFoundException {
@@ -475,4 +487,28 @@ public void testInvalidSecondaryKey() {
475487
.verify();
476488
}
477489

490+
@Test
491+
public void testRunQueryWithSimpleReturnType() {
492+
Criteria ageBetween = Criteria.getInstance(CriteriaType.BETWEEN, "age", Arrays.asList(AGE - 1, AGE + 1),
493+
Part.IgnoreCaseType.NEVER);
494+
final SqlQuerySpec sqlQuerySpec = new FindQuerySpecGenerator().generateCosmos(new CosmosQuery(ageBetween));
495+
final Flux<Person> flux = cosmosTemplate.runQuery(sqlQuerySpec, Person.class, Person.class);
496+
497+
StepVerifier.create(flux).expectNextCount(1).verifyComplete();
498+
}
499+
500+
@Test
501+
public void testRunQueryWithReturnTypeContainingLocalDateTime() {
502+
final AuditableEntity entity = new AuditableEntity();
503+
entity.setId(UUID.randomUUID().toString());
504+
505+
auditableRepository.save(entity);
506+
507+
Criteria equals = Criteria.getInstance(CriteriaType.IS_EQUAL, "id", Collections.singletonList(entity.getId()), Part.IgnoreCaseType.NEVER);
508+
final SqlQuerySpec sqlQuerySpec = new FindQuerySpecGenerator().generateCosmos(new CosmosQuery(equals));
509+
final Flux<AuditableEntity> flux = cosmosTemplate.runQuery(sqlQuerySpec, AuditableEntity.class, AuditableEntity.class);
510+
511+
StepVerifier.create(flux).expectNextCount(1).verifyComplete();
512+
}
513+
478514
}

sdk/cosmos/azure-spring-data-cosmos-test/src/test/java/com/azure/spring/data/cosmos/repository/StubAuditorProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
public class StubAuditorProvider implements AuditorAware<String> {
1010

11-
private String currentAuditor;
11+
private String currentAuditor = "auditor";
1212

1313
@Override
1414
public Optional<String> getCurrentAuditor() {

sdk/cosmos/azure-spring-data-cosmos-test/src/test/java/com/azure/spring/data/cosmos/repository/StubDateTimeProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
public class StubDateTimeProvider implements DateTimeProvider {
1212

13-
private OffsetDateTime now;
13+
private OffsetDateTime now = OffsetDateTime.now();
1414

1515
@Override
1616
public Optional<TemporalAccessor> getNow() {

sdk/cosmos/azure-spring-data-cosmos-test/src/test/java/com/azure/spring/data/cosmos/repository/integration/BookRepositoryIT.java renamed to sdk/cosmos/azure-spring-data-cosmos-test/src/test/java/com/azure/spring/data/cosmos/repository/integration/AnnotatedQueryIT.java

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44

55
import com.azure.spring.data.cosmos.common.TestConstants;
66
import com.azure.spring.data.cosmos.core.CosmosTemplate;
7+
import com.azure.spring.data.cosmos.domain.AuditableEntity;
78
import com.azure.spring.data.cosmos.domain.Book;
9+
import com.azure.spring.data.cosmos.repository.StubAuditorProvider;
810
import com.azure.spring.data.cosmos.repository.TestRepositoryConfig;
11+
import com.azure.spring.data.cosmos.repository.repository.AuditableRepository;
912
import com.azure.spring.data.cosmos.repository.repository.BookRepository;
1013
import com.azure.spring.data.cosmos.repository.support.CosmosEntityInformation;
1114
import org.junit.After;
@@ -22,10 +25,12 @@
2225
import java.util.UUID;
2326

2427
import static org.assertj.core.api.Assertions.assertThat;
28+
import static org.junit.Assert.assertNotNull;
29+
import static org.junit.Assert.assertTrue;
2530

2631
@RunWith(SpringJUnit4ClassRunner.class)
2732
@ContextConfiguration(classes = TestRepositoryConfig.class)
28-
public class BookRepositoryIT {
33+
public class AnnotatedQueryIT {
2934

3035
private static final Book TEST_BOOK_1 = new Book(TestConstants.ID_1, UUID.randomUUID().toString(),
3136
"title1");
@@ -42,7 +47,13 @@ public class BookRepositoryIT {
4247
private CosmosTemplate template;
4348

4449
@Autowired
45-
private BookRepository repository;
50+
private BookRepository bookRepository;
51+
52+
@Autowired
53+
private AuditableRepository auditableRepository;
54+
55+
@Autowired
56+
private StubAuditorProvider stubAuditorProvider;
4657

4758
@AfterClass
4859
public static void afterClassCleanup() {
@@ -55,20 +66,35 @@ public void setUp() {
5566
staticTemplate = template;
5667
template.createContainerIfNotExists(entityInformation);
5768
}
58-
repository.saveAll(Arrays.asList(TEST_BOOK_1, TEST_BOOK_2));
5969
isSetupDone = true;
6070
}
6171

6272
@After
6373
public void cleanup() {
64-
repository.deleteAll();
74+
bookRepository.deleteAll();
6575
}
6676

6777
@Test
6878
public void testAnnotatedQuery() {
69-
final List<Book> result = repository.annotatedFindBookById(TEST_BOOK_1.getId());
79+
bookRepository.saveAll(Arrays.asList(TEST_BOOK_1, TEST_BOOK_2));
80+
81+
final List<Book> result = bookRepository.annotatedFindBookById(TEST_BOOK_1.getId());
7082
assertThat(result).isNotNull();
7183
assertThat(result.size()).isEqualTo(1);
7284
assertThat(result.get(0).getId()).isEqualTo(TEST_BOOK_1.getId());
7385
}
86+
87+
@Test
88+
public void testAnnotatedQueryWithReturnTypeContainingLocalDateTime() {
89+
final AuditableEntity entity = new AuditableEntity();
90+
entity.setId(UUID.randomUUID().toString());
91+
92+
final AuditableEntity savedEntity = auditableRepository.save(entity);
93+
94+
final List<AuditableEntity> result = auditableRepository.annotatedFindById(savedEntity.getId());
95+
assertThat(result).isNotNull();
96+
assertThat(result.size()).isEqualTo(1);
97+
assertThat(result.get(0).getId()).isEqualTo(entity.getId());
98+
}
99+
74100
}

sdk/cosmos/azure-spring-data-cosmos-test/src/test/java/com/azure/spring/data/cosmos/repository/repository/AuditableRepository.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@
44

55
import com.azure.spring.data.cosmos.domain.AuditableEntity;
66
import com.azure.spring.data.cosmos.repository.CosmosRepository;
7+
import com.azure.spring.data.cosmos.repository.Query;
8+
import org.springframework.data.repository.query.Param;
9+
10+
import java.util.List;
711

812
public interface AuditableRepository extends CosmosRepository<AuditableEntity, String> {
13+
14+
@Query("select * from r where r.id = @id")
15+
List<AuditableEntity> annotatedFindById(@Param("id") String id);
16+
917
}

sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/core/CosmosTemplate.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -747,7 +747,8 @@ public MappingCosmosConverter getConverter() {
747747

748748
@Override
749749
public <T> Iterable<T> runQuery(SqlQuerySpec querySpec, Class<?> domainType, Class<T> returnType) {
750-
return getJsonNodeFluxFromQuerySpec(getContainerName(domainType), querySpec, returnType)
750+
return getJsonNodeFluxFromQuerySpec(getContainerName(domainType), querySpec)
751+
.map(jsonNode -> toDomainObject(returnType, jsonNode))
751752
.collectList()
752753
.block();
753754
}
@@ -805,15 +806,15 @@ private Flux<JsonNode> findItemsAsFlux(@NonNull CosmosQuery query,
805806
CosmosExceptionUtils.exceptionHandler("Failed to find items", throwable));
806807
}
807808

808-
private <T> Flux<T> getJsonNodeFluxFromQuerySpec(
809-
@NonNull String containerName, SqlQuerySpec sqlQuerySpec, Class<T> classType) {
809+
private Flux<JsonNode> getJsonNodeFluxFromQuerySpec(
810+
@NonNull String containerName, SqlQuerySpec sqlQuerySpec) {
810811
final CosmosQueryRequestOptions cosmosQueryRequestOptions = new CosmosQueryRequestOptions();
811812
cosmosQueryRequestOptions.setQueryMetricsEnabled(this.queryMetricsEnabled);
812813

813814
return cosmosAsyncClient
814815
.getDatabase(this.databaseName)
815816
.getContainer(containerName)
816-
.queryItems(sqlQuerySpec, cosmosQueryRequestOptions, classType)
817+
.queryItems(sqlQuerySpec, cosmosQueryRequestOptions, JsonNode.class)
817818
.byPage()
818819
.publishOn(Schedulers.parallel())
819820
.flatMap(cosmosItemFeedResponse -> {

sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/core/ReactiveCosmosTemplate.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -600,11 +600,16 @@ public MappingCosmosConverter getConverter() {
600600

601601
@Override
602602
public <T> Flux<T> runQuery(SqlQuerySpec querySpec, Class<?> domainType, Class<T> returnType) {
603+
return runQuery(querySpec, domainType)
604+
.map(cosmosItemProperties -> toDomainObject(returnType, cosmosItemProperties));
605+
}
606+
607+
private Flux<JsonNode> runQuery(SqlQuerySpec querySpec, Class<?> domainType) {
603608
String containerName = getContainerName(domainType);
604609
CosmosQueryRequestOptions options = new CosmosQueryRequestOptions();
605610
return cosmosAsyncClient.getDatabase(this.databaseName)
606611
.getContainer(containerName)
607-
.queryItems(querySpec, options, returnType)
612+
.queryItems(querySpec, options, JsonNode.class)
608613
.byPage()
609614
.publishOn(Schedulers.parallel())
610615
.flatMap(cosmosItemFeedResponse -> {

0 commit comments

Comments
 (0)