Skip to content
This repository has been archived by the owner on Jan 19, 2022. It is now read-only.

Firestore - automatic id generation #2466

Merged
merged 10 commits into from
Jul 20, 2020
Merged

Firestore - automatic id generation #2466

merged 10 commits into from
Jul 20, 2020

Conversation

dmitry-s
Copy link
Contributor

fixes #2428

@codecov
Copy link

codecov bot commented Jul 15, 2020

Codecov Report

Merging #2466 into master will decrease coverage by 7.20%.
The diff coverage is 90.00%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master    #2466      +/-   ##
============================================
- Coverage     81.29%   74.09%   -7.21%     
+ Complexity     2343     2125     -218     
============================================
  Files           264      265       +1     
  Lines          7663     7678      +15     
  Branches        790      794       +4     
============================================
- Hits           6230     5689     -541     
- Misses         1097     1620     +523     
- Partials        336      369      +33     
Flag Coverage Δ Complexity Δ
#integration ? ?
#unittests 74.09% <90.00%> (+0.03%) 2125.00 <6.00> (+6.00)

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ Complexity Δ
...rk/cloud/gcp/data/firestore/FirestoreTemplate.java 75.00% <85.71%> (-18.80%) 52.00 <3.00> (-12.00)
...ringframework/cloud/gcp/data/firestore/AutoId.java 100.00% <100.00%> (ø) 3.00 <3.00> (?)
...gcp/secretmanager/SecretManagerPropertySource.java 0.00% <0.00%> (-100.00%) 0.00% <0.00%> (-4.00%)
...a/spanner/repository/query/SpannerQueryMethod.java 0.00% <0.00%> (-100.00%) 0.00% <0.00%> (-6.00%)
...retmanager/SecretManagerPropertySourceLocator.java 0.00% <0.00%> (-100.00%) 0.00% <0.00%> (-2.00%)
...figure/config/GcpConfigBootstrapConfiguration.java 0.00% <0.00%> (-100.00%) 0.00% <0.00%> (-2.00%)
...e/spanner/GcpSpannerEmulatorAutoConfiguration.java 0.00% <0.00%> (-100.00%) 0.00% <0.00%> (-2.00%)
...epository/config/SpannerRepositoriesRegistrar.java 0.00% <0.00%> (-100.00%) 0.00% <0.00%> (-3.00%)
...ository/config/DatastoreRepositoriesRegistrar.java 0.00% <0.00%> (-100.00%) 0.00% <0.00%> (-3.00%)
...restore/GcpFirestoreEmulatorAutoConfiguration.java 0.00% <0.00%> (-84.85%) 0.00% <0.00%> (-4.00%)
... and 56 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 853a949...6de9ca1. Read the comment docs.

@@ -23,6 +23,7 @@
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-firestore</artifactId>
<version>1.35.2-SNAPSHOT</version>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix indentation here; tab/space mismatch.

return Flux.from(entities).flatMap(this::create);
}

private <T> Mono<T> create(T entity) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to add a javadoc for this one describing purpose of method something like "Creates a single (non-batched) document in Firestore; ID generation is only available for non-batched created documents."

return ObservableReactiveUtil.streamingBidirectionalCall(
this::openWriteStream, inputs, this::buildWriteRequest);
return Flux.from(instances)
.groupBy(t -> getIdValue(t) == null).flatMap(groupedFlux ->
Copy link
Contributor

@dzou dzou Jul 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nit, would prefer if you can format this section so each operator gets its own line like this:

return  Flux.from(instances)
    .groupBy(t -> getIdValue(t) == null)
    .flatMap(groupedFlux -> groupedFlux.key() ? create(groupedFlux) : upsert(groupedFlux));

Clever usage of Group By!

}

public FirestoreClassMapper getClassMapper() {
return this.classMapper;
}

private static class ResourceName {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see; I would avoid introducing a new class here if possible -

I think if you include it, you will have to write a lot more docs to describe what it means for future readers of this code - i.e. it would at least require a class-level javadoc, and also - generated is too general (i.e. generated what?); you may have to rename to needsGeneratedId or add comment; similarly name is too general as well - i.e. (name of what?)

It's not worth the burden of adding this class in my opinion if it is avoidable.

return Write.newBuilder()
.setUpdate(document)
.build();
ResourceName resourceName = buildResourceName(entity);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would avoid introducing the ResourceName class to avoid coupling the resource name with whether the id was generated or not. Also mentioned below; I think it's not worth the documentation burden.

Instead, maybe refactor so it's like:

Object idVal = getOrGenerateEntityId(entity);
String resourceName = buildResourceName(Class<?> entityType, Object idVal);

@dmitry-s dmitry-s changed the title automatic id generation Firestore - automatic id generation Jul 16, 2020
@dmitry-s dmitry-s marked this pull request as ready for review July 16, 2020 22:10
@dmitry-s dmitry-s requested a review from dzou July 16, 2020 22:10
Copy link
Contributor

@meltsufin meltsufin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Just some minor polish suggestions.

String resourceName = buildResourceName(entity);
Document document = getClassMapper().entityToDocument(entity, resourceName);
Builder builder = Write.newBuilder().setUpdate(document);
if (needsAutoId) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really see the point of having this needsAutoId local variable, since it's only used once.
I would just put getIdValue(entity) == null directly in the if.


//TODO: replace with Internal.autoId() when it is available
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indicate the package of Internal? Otherwise, it's ambiguous.

}

private <T> String buildResourceName(T entity) {
FirestorePersistentEntity<?> persistentEntity =
this.mappingContext.getPersistentEntity(entity.getClass());
FirestorePersistentProperty idProperty = persistentEntity.getIdPropertyOrFail();
Object idVal = persistentEntity.getPropertyAccessor(entity).getProperty(idProperty);
if (idVal == null) {
if (idProperty.getType() != String.class) {
throw new FirestoreDataException("Automatic ID generation only supported for String type");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also say that id was null and what its type was to make the exception more clear.

}

public FirestoreClassMapper getClassMapper() {
return this.classMapper;
}

/** Creates a pseudo-random 20-character ID that can be used for Firestore documents. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you indicate where it was copied from?

.putAllFields(valuesMap)
.setName(documentResourceName).build();
Builder builder = Document.newBuilder().putAllFields(valuesMap);
if (documentResourceName != null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it now possible for documentResourceName to be null?


//Creates a pseudo-random 20-character ID that can be used for Firestore documents.
//Copied from com.google.cloud.firestore.FirestoreImpl
private static String autoId() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you move the Id generating helper method autoId() to a package-private class? maybe something like FirestoreIdGenerator.

This way you can move all the code and fields in there related to ID generation; which allows you to remember what to delete once com.google.cloud.firestore.Internal.autoId() becomes available. Otherwise you will have to comb through the code a little bit more in the future to do this removal.

@dmitry-s dmitry-s requested review from dzou and meltsufin July 17, 2020 19:03
Copy link
Contributor

@dzou dzou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good; just some minor nits.

import java.security.SecureRandom;
import java.util.Random;

//Copied from com.google.cloud.firestore.FirestoreImpl
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For new classes, add a top level Javadoc :) i.e. with the author tag and brief description etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's why I didn't want to introduce a new class :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lol sorry for the inconvenience. At least it will be deleted shortly though! (Once they expose auto id).

return Document.newBuilder()
.putAllFields(valuesMap)
.setName(documentResourceName).build();
Builder builder = Document.newBuilder().putAllFields(valuesMap);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might as well just revert this file then if there are no changes.

Copy link
Contributor

@elefeint elefeint left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good from my side.

@sonarcloud
Copy link

sonarcloud bot commented Jul 20, 2020

Kudos, SonarCloud Quality Gate passed!

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities (and Security Hotspot 0 Security Hotspots to review)
Code Smell A 1 Code Smell

92.9% 92.9% Coverage
0.0% 0.0% Duplication

warning The version of Java (1.8.0_151) you have used to run this analysis is deprecated and we will stop accepting it from October 2020. Please update to at least Java 11.
Read more here

@dmitry-s dmitry-s requested review from dzou and elefeint July 20, 2020 19:37
@dmitry-s dmitry-s merged commit e6def07 into master Jul 20, 2020
@dmitry-s dmitry-s deleted the auto-id-generation branch July 20, 2020 20:18
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Firestore - DocumentId autogeneration
4 participants