GoogleCloudPlatform / GoogleCloudPlatform/spring-cloud-gcp
Request for GenericTypeIndicator Support in spring-cloud-gcp-starter-data-firestore for Handling Generic Types
- Dominant language
- Java
- Stars
- 551
- Forks
- 349
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 14
Description
## Problem
I am implementing a generic Idempotency handling mechanism in my service using Firestore. However, I encountered a serialization/deserialization issue when dealing with generic types. The absence of GenericTypeIndicator in the Firestore integration for Spring makes it challenging to implement a clean and efficient solution for generic data handling.
## Example of Current Implementation:
#### Domain
```java
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collectionName = "idempotencies")
public class Idempotency {
@DocumentId
private String key;
private T response;
private LocalDateTime expiryDate;
public Idempotency(String key, T response) {
this.key = key;
this.response = response;
}
}
```
#### Service
```java
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class IdempotencyService {
private final IdempotencyRepository repository;
public Mono checkIdempotency(String idempotencyKey, Class type) {
return repository.findById(idempotencyKey)
.filter(this::isIdempotencyValid)
.map(Idempotency::getResponse)
.cast(type)
.doOnSuccess(response -> {
if (response != null) {
log.info("Idempotency key {} triggered, returning saved value: {}.", idempotencyKey, response);
} else {
log.info("Idempotency key {} triggered, but no response to return (No value stored or key expired).", idempotencyKey);
}
})
.doOnError(error -> log.error("Error checking idempotency for key {}: {}", idempotencyKey, error.getMessage()));
}
// ... Other generic methods
```
#### Problem
```
2024-04-13 23:24:12.782 [http-nio-0.0.0.0-8084-exec-2] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.RuntimeException: Could not deserialize object. Class com.myvrcloud.user.domain.Idempotency has generic type parameters, please use GenericTypeIndicator instead] with root cause
java.lang.RuntimeException: Could not deserialize object. Class com.myvrcloud.user.domain.Idempotency has generic type parameters, please use GenericTypeIndicator instead
```
## Proposed Solution:
I suggest adding GenericTypeIndicator or similar functionality to `spring-cloud-gcp-starter-data-firestore` to facilitate the handling of generic types. This feature is crucial for developing robust and type-safe applications and would significantly enhance the usability of Firestore with Spring.
## Possible Workaround
Currently, I am serializing the generic type to a JSON string and deserializing it manually. While this approach works, it is cumbersome and detracts from the clean, type-safe approach facilitated by Spring Data. Something like:
```java
public Mono checkIdempotency(String idempotencyKey, Class type) {
return repository.findById(idempotencyKey)
.filter(this::isIdempotencyValid)
.flatMap(idempotency -> {
try {
T response = objectMapper.readValue(idempotency.getResponse(), type);
return Mono.just(response);
} catch (Exception e) {
log.error("Error deserializing response", e);
return Mono.error(e);
}
});
}
```
Or something like:
```java
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collectionName = "idempotencies")
public class Idempotency {
@DocumentId
private String key;
@Exclude
@Getter(onMethod_ = @Exclude)
@Setter(onMethod_ = @Exclude)
private String responseJson;
private LocalDateTime expiryDate;
@Exclude
private Object response;
public Idempotency(String key, Object response) {
this.key = key;
this.setResponse(response);
}
@PropertyName("response")
public void setResponse(Object response) {
try {
this.responseJson = JsonUtil.getMapper().writeValueAsString(response);
this.response = response;
} catch (JsonProcessingException e) {
throw new RuntimeException("Failed to serialize response", e);
}
}
@PropertyName("response")
public T getResponse(Class type) {
try {
return new ObjectMapper().readValue(this.responseJson, type);
} catch (JsonProcessingException e) {
throw new RuntimeException("Failed to deserialize response", e);
}
}
}
// checkIdempotency method in the Service will need to use the method...
repository.findById(idempotencyKey)
.filter(this::isIdempotencyValid)
.map(idempotency -> idempotency.getResponse(type))
//...
```
## Conclusion
The inclusion of generic type handling in Firestore's Spring Data integration would align it with other parts of the Spring ecosystem, which already support generics effectively. This enhancement would greatly simplify the development process and improve code quality (and avoid the need for "workarounds").
Contributor guide
Research direction
Start by reviewing the spring-cloud-gcp-starter-data-firestore integration and how its current deserialization handles classes with generic type parameters. Define the supported GenericTypeIndicator-style API and its expected behavior before identifying tests for generic Firestore entities; done means generic types deserialize without the JSON workaround.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, spring
- Domain
- backend, database
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100