-
Notifications
You must be signed in to change notification settings - Fork 894
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make Timestamp a part of the public Firestore API. #544
Conversation
This is a transitional state; eventually, Timestamp will live in a new separate package (common-types/). Also: - move Timestamp.fromISOString method into Serializer, the only place of usage; - rename nanos to nanoseconds.
Truncate Timestamps to microseconds immediately upon receiving them as a parameter, so that offline and online behavior is consistent.
The behavior to return timestamp fields as dates is now legacy. It's on by default to allow for a transitional period. The new behavior to return Timestamp objects instead is opt-in, but will become default in the future. The advantage Timestamps have is higher precision than native browser Date. If the new behavior is not enabled, a warning is logged on startup urging users to upgrade.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Michael,
I decided to do this as a single PR, partially because it's similar to other platforms that were reviewed already, and partially because line count is lower in TypeScript. The PR is split into 3 meaningful commits. If you'd rather have me split this into 3 PRs, I'll do it.
The one thing I'm not sure about is the suggestion from API review that Timestamp should live in a common-types/ folder. Currently, all such folders come in pairs, e.g. firestore/ and firestore-types/. I'm not sure if it should be common/ and common-types/?
packages/firestore-types/index.d.ts
Outdated
* Creates a new timestamp from the given number of milliseconds since Unix | ||
* epoch 1970-01-01T00:00:00Z. | ||
*/ | ||
static fromMillis(milliseconds: number): Timestamp; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed the name to match the API review doc, let me know if you think we should try to push back on this.
@@ -296,6 +321,32 @@ export class Firestore implements firestore.FirebaseFirestore, FirebaseService { | |||
'FirestoreSettings.host cannot be falsey' | |||
); | |||
|
|||
if (!this._config.settings.timestampsInSnapshotsEnabled) { | |||
log.error(` | |||
The default behavior for Date objects stored in Firestore is going to change |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- After seeing this message in the logs, wrapping it to 80 columns seemed like a good idea.
- This message gets logged about a thousand times during tests. It's super-annoying, do you have any suggestions on how to disable it during a test run?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Good call.
- Can we just set it to true, at least for the majority of our tests? That would be desirable, since that'll be the eventual default anyway.
FWIW- since this is javascript, we could have tests do hackiness like:
let oldConsoleError = console.error;
console.error = () => { };
...
console.error = oldConsoleError;
That might be okay if there was a very well-defined scope where we wanted to suppress the log message, but it's probably not a great solution. :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Do you think this applies to other platforms as well? I'm not sure how developers usually read logs there.
- Setting it to true sounds good, but I'd like to clarify before implementing: it leads to breakage of 11 existing tests, mostly around server timestamps. Do you think the tests should be changed to anticipate the new behavior, or should I specifically use the old behavior in these tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Hrm. I suspect wrapping to 80 columns would be friendly on the other platforms, though I don't remember if Xcode and Android Studio wrap automatically in their consoles or not. So when you revisit the log messages pending API review feedback, etc., maybe wrap them, but I am not too concerned about it.
- I think the 11 tests should be changed to expect the new behavior, but I'd make sure we have at least one test that verifies server timestamps return Dates (for ServerTimestampBehavior 'estimate') if you have the enableTimestampsInSnapshots setting turned off.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed the tests, including one test for server timestamps that previously checked for Timestamps if the setting is true, but now checks for Dates if setting is false.
@@ -387,7 +438,9 @@ export class Firestore implements firestore.FirebaseFirestore, FirebaseService { | |||
if (this._firestoreClient) { | |||
return this._firestoreClient.shutdown(); | |||
} | |||
} | |||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if this should be in INTERNAL. My reasoning was that it seems that in TypeScript, current settings aren't exposed (or I just missed it).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
INTERNAL is semi-public. So as-is users of the SDK could access this via firebase.firestore().INTERNAL.areTimestampsInSnapshotsEnabled() which we don't really want. So I think instead I'd make this a property on Firestore:
get _areTimestampsInSnapshotsEnabled(): boolean {
return this._config.settings.timestampsInSnapshotsEnabled;
}
Note that we use '_' for any members of our public API classes (like Firestore) that we don't want to be visible in our public API. There's a minification step as part of our build that will rename '_blah' properties to obfuscated names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, thanks for the explanation.
|
||
constructor(readonly seconds: number, readonly nanoseconds: number) { | ||
if (nanoseconds < 0) { | ||
throw new FirestoreError( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use throw
instead of assert
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if this comment is asking a question, but I think throw is appropriate since this is a user-facing error rather than an internal assertion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for not being clear, I just wanted to draw attention to the change.
@@ -69,21 +69,32 @@ export enum ServerTimestampBehavior { | |||
|
|||
/** Holds properties that define field value deserialization options. */ | |||
export class FieldValueOptions { | |||
static readonly defaultOptions = new FieldValueOptions( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed defaultOptions
like elsewhere.
@@ -83,6 +83,9 @@ const OPERATORS = (() => { | |||
return ops; | |||
})(); | |||
|
|||
// A RegExp matching ISO 8601 UTC timestamps with optional fraction. | |||
const ISO_REG_EXP = new RegExp(/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.(\d+))?Z$/); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I originally moved everything connected to parsing ISO strings here to avoid exposing this functionality in the public API. I guess it was possible to leave it on the Timestamp and not declare it in index.d.ts
file (at least I assume so), but on the other hand, that functionality is only used in the serializer. Feel free to push back on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it would be fine to have it in Timestamp but not expose it in the .d.ts. Though we would also want to prefix the APIs with '_' to ensure they're minified. That said, it seems like ISO timestamp conversion is specific to the web serializer implementation (we don't have iso timestamp conversion in the iOS / Android Timestamp.java implementation) and so keeping it in the serializer sounds good to me.
@@ -86,6 +86,11 @@ export function toDataArray( | |||
return docSet.docs.map(d => d.data()); | |||
} | |||
|
|||
/** Converts a DocumentSet to an array with the id of each document */ | |||
export function toIds(docSet: firestore.QuerySnapshot): String[] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a similar helper function on iOS and Android (I hope I didn't miss it here).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's possible it was omitted because map() is so easy in JS, but I'm not opposed to having it.
}); | ||
}); | ||
|
||
it('fromISOString', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to serializer tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome. I haven't reviewed the code yet but I preemptively replied to a couple of your comments. As for where to put Timestamp (creating a common/ and common-types/ directory, etc.), that is a question for @jshcrowthe. Josh, is this on your radar? We've been asked to contribute Timestamp
as a "common" type to be used by Firestore but potentially other parts of the Firebase SDK in the future (ping me or @var-const for more details).
@@ -387,7 +438,9 @@ export class Firestore implements firestore.FirebaseFirestore, FirebaseService { | |||
if (this._firestoreClient) { | |||
return this._firestoreClient.shutdown(); | |||
} | |||
} | |||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
INTERNAL is semi-public. So as-is users of the SDK could access this via firebase.firestore().INTERNAL.areTimestampsInSnapshotsEnabled() which we don't really want. So I think instead I'd make this a property on Firestore:
get _areTimestampsInSnapshotsEnabled(): boolean {
return this._config.settings.timestampsInSnapshotsEnabled;
}
Note that we use '_' for any members of our public API classes (like Firestore) that we don't want to be visible in our public API. There's a minification step as part of our build that will rename '_blah' properties to obfuscated names.
|
||
constructor(readonly seconds: number, readonly nanoseconds: number) { | ||
if (nanoseconds < 0) { | ||
throw new FirestoreError( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if this comment is asking a question, but I think throw is appropriate since this is a user-facing error rather than an internal assertion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks great. Have a few minor requests, mostly around comments, etc.
packages/firestore-types/index.d.ts
Outdated
* DocumentSnapshot}s. | ||
* | ||
* Currently, Firestore returns timestamp fields as {@link Date} but {@link | ||
* Date} only supports millisecond precision, which leads to truncation and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting! I didn't know @link was available in JSDoc / TypeDoc (this is TypeScript so in theory we'd use something like TypeDoc, but in practice we end up copying these comments into an externs file and generating docs via JSDoc :-/). That said, I'd be surprised if {@link Date} will do anything useful, since Date isn't defined in the codebase (and there isn't a canonical location for docs on JavaScript standard types). So I'd be inclined to do Date or "a JavaScript Date object" or similar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Examining the file, it appears that class names are just wrapped in code tags ``. Changed all usages of @link to this. What do you think about using Date and wrapping it in backticks?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that seems fine and consistent with what we've been doing. Using @link
might be a good idea, but we can tackle that holistically at a later time.
packages/firestore-types/index.d.ts
Outdated
@@ -20,21 +20,45 @@ import { FirebaseApp, FirebaseNamespace } from '@firebase/app-types'; | |||
* Document data (for use with `DocumentReference.set()`) consists of fields | |||
* mapped to values. | |||
*/ | |||
export type DocumentData = { [field: string]: any }; | |||
export type DocumentData = { | |||
[field: string]: any; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you intentionally add these semicolons? I don't care one way or the other. I'm surprised that it's apparently optional.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure this is from the precommit code formatting. I'll have to figure out why it reformats so much before merging; rerunning yarn
didn't seem to help.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I see. It looks like if I remove the semicolon, prettier adds it back, but if I completely undo this change (remove the semicolon and put this back to being one line) then prettier leaves it as-is... Maybe you ended up formatting with a "wrong" version and then the "right" version, which had a different net effect than just formatting with the "right" version or something? I don't know... Anyway, I'm not inclined to worry about it for this PR...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, this sounds like it could be the cause. I first formatted using clang-format
, which resulted in a pretty different formatting, and I guess prettier
couldn't restore it successfully.
packages/firestore-types/index.d.ts
Outdated
*/ | ||
static now(): Timestamp; | ||
|
||
/** Creates a new timestamp from the given date. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add the @param
/ @return
comments? I'm happy to omit them in our normal codebase, but this .d.ts file (indirectly) ends up being used to generate docs, so I think we want to be complete.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
These comments become pretty repetitive, please let me know what you think.
packages/firestore-types/index.d.ts
Outdated
/** | ||
* Creates a new timestamp. | ||
* | ||
* @param seconds represents seconds of UTC time since Unix epoch |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think rather than "seconds represents seconds ..." we probably want "seconds The number of seconds ..." or similar to match our other comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
packages/firestore-types/index.d.ts
Outdated
*/ | ||
toMillis(): number; | ||
|
||
compareTo(other: Timestamp): number; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should expose compareTo() in our public API. We don't on any of our other types today.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -86,6 +86,11 @@ export function toDataArray( | |||
return docSet.docs.map(d => d.data()); | |||
} | |||
|
|||
/** Converts a DocumentSet to an array with the id of each document */ | |||
export function toIds(docSet: firestore.QuerySnapshot): String[] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's possible it was omitted because map() is so easy in JS, but I'm not opposed to having it.
const makeTimestamp = (seconds, micros) => | ||
new Timestamp(seconds, micros * 1000); | ||
|
||
it('can accept Timestamps as limits', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if this was copied from elsewhere, but since we have a limit() API, this would be clearer as "can accept Timestamps as bounds" or similar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Yes, I used similar test names on other platforms, I guess I'll change them to refer to "bounds" as well.
longitude: 4.56 | ||
} | ||
}; | ||
const expected = { geoPointValue: { latitude: 1.23, longitude: 4.56 } }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious if you reformatted these or prettier did it for some reason...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prettier
is the culprit!
return this.seconds * 1000 + this.nanoseconds / 1e6; | ||
} | ||
|
||
compareTo(other: Timestamp): number { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming we don't make this public, can you rename to _compareTo()?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Does it apply to isEqual
as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isEqual() is considered part of our public API and so we don't want it minified and so it should stay isEqual(), not _isEqual().
packages/firestore-types/index.d.ts
Outdated
|
||
isEqual(other: Timestamp): boolean; | ||
|
||
toString(): string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
toString() exists on any Object in JavaScript and doesn't need to be explicitly listed here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One more question: it looks like dist/ folder contains build artifacts. Should I update it? If yes, how do I do it?
packages/firestore-types/index.d.ts
Outdated
@@ -20,21 +20,45 @@ import { FirebaseApp, FirebaseNamespace } from '@firebase/app-types'; | |||
* Document data (for use with `DocumentReference.set()`) consists of fields | |||
* mapped to values. | |||
*/ | |||
export type DocumentData = { [field: string]: any }; | |||
export type DocumentData = { | |||
[field: string]: any; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure this is from the precommit code formatting. I'll have to figure out why it reformats so much before merging; rerunning yarn
didn't seem to help.
packages/firestore-types/index.d.ts
Outdated
* DocumentSnapshot}s. | ||
* | ||
* Currently, Firestore returns timestamp fields as {@link Date} but {@link | ||
* Date} only supports millisecond precision, which leads to truncation and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Examining the file, it appears that class names are just wrapped in code tags ``. Changed all usages of @link to this. What do you think about using Date and wrapping it in backticks?
@@ -127,6 +128,8 @@ class FirestoreSettings { | |||
/** Whether to use SSL when connecting. */ | |||
ssl: boolean; | |||
|
|||
timestampsInSnapshotsEnabled: boolean; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
} | ||
|
||
isEqual(other: FirestoreSettings): boolean { | ||
return ( | ||
this.host === other.host && | ||
this.ssl === other.ssl && | ||
this.timestampsInSnapshotsEnabled == other.timestampsInSnapshotsEnabled && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
|
||
constructor(readonly seconds: number, readonly nanoseconds: number) { | ||
if (nanoseconds < 0) { | ||
throw new FirestoreError( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for not being clear, I just wanted to draw attention to the change.
packages/firestore-types/index.d.ts
Outdated
*/ | ||
static now(): Timestamp; | ||
|
||
/** Creates a new timestamp from the given date. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
These comments become pretty repetitive, please let me know what you think.
packages/firestore-types/index.d.ts
Outdated
/** | ||
* Creates a new timestamp. | ||
* | ||
* @param seconds represents seconds of UTC time since Unix epoch |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
packages/firestore-types/index.d.ts
Outdated
|
||
compareTo(other: Timestamp): number; | ||
|
||
isEqual(other: Timestamp): boolean; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -387,7 +438,9 @@ export class Firestore implements firestore.FirebaseFirestore, FirebaseService { | |||
if (this._firestoreClient) { | |||
return this._firestoreClient.shutdown(); | |||
} | |||
} | |||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, thanks for the explanation.
settings.timestampsInSnapshotsEnabled, | ||
DEFAULT_TIMESTAMPS_IN_SNAPSHOTS_ENABLED | ||
); | ||
this.timestampsInSnapshotsEnabled = settings.timestampsInSnapshotsEnabled; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, thanks for spotting it.
- add additional comments in Timestamp declarations; - remove unnecessary method declarations from Timestamp; - rename timestampsInSnapshotsEnabled -> timestampsInSnapshots; - use timestampsInSnapshots = true in tests; - small code cleanup.
4f58e9d
to
ff1583d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Changes look good pending the remaining test fixup...
packages/firestore-types/index.d.ts
Outdated
* DocumentSnapshot}s. | ||
* | ||
* Currently, Firestore returns timestamp fields as {@link Date} but {@link | ||
* Date} only supports millisecond precision, which leads to truncation and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that seems fine and consistent with what we've been doing. Using @link
might be a good idea, but we can tackle that holistically at a later time.
packages/firestore-types/index.d.ts
Outdated
@@ -20,21 +20,45 @@ import { FirebaseApp, FirebaseNamespace } from '@firebase/app-types'; | |||
* Document data (for use with `DocumentReference.set()`) consists of fields | |||
* mapped to values. | |||
*/ | |||
export type DocumentData = { [field: string]: any }; | |||
export type DocumentData = { | |||
[field: string]: any; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I see. It looks like if I remove the semicolon, prettier adds it back, but if I completely undo this change (remove the semicolon and put this back to being one line) then prettier leaves it as-is... Maybe you ended up formatting with a "wrong" version and then the "right" version, which had a different net effect than just formatting with the "right" version or something? I don't know... Anyway, I'm not inclined to worry about it for this PR...
@@ -296,6 +321,32 @@ export class Firestore implements firestore.FirebaseFirestore, FirebaseService { | |||
'FirestoreSettings.host cannot be falsey' | |||
); | |||
|
|||
if (!this._config.settings.timestampsInSnapshotsEnabled) { | |||
log.error(` | |||
The default behavior for Date objects stored in Firestore is going to change |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Hrm. I suspect wrapping to 80 columns would be friendly on the other platforms, though I don't remember if Xcode and Android Studio wrap automatically in their consoles or not. So when you revisit the log messages pending API review feedback, etc., maybe wrap them, but I am not too concerned about it.
- I think the 11 tests should be changed to expect the new behavior, but I'd make sure we have at least one test that verifies server timestamps return Dates (for ServerTimestampBehavior 'estimate') if you have the enableTimestampsInSnapshots setting turned off.
return this.seconds * 1000 + this.nanoseconds / 1e6; | ||
} | ||
|
||
compareTo(other: Timestamp): number { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isEqual() is considered part of our public API and so we don't want it minified and so it should stay isEqual(), not _isEqual().
Also add a test to make sure server timestamps retain the old behavior if the global settings if not set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will hold off merging until API review passes. Also, I have a question: it looks like dist/ folder contains build artifacts, do I have to/how do I update its contents?
@@ -157,12 +165,29 @@ class FirestoreSettings { | |||
settings.credentials | |||
); | |||
this.credentials = settings.credentials; | |||
|
|||
if (settings.timestampsInSnapshotsEnabled === undefined) { | |||
this.timestampsInSnapshotsEnabled = DEFAULT_TIMESTAMPS_IN_SNAPSHOTS_ENABLED; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -296,6 +321,32 @@ export class Firestore implements firestore.FirebaseFirestore, FirebaseService { | |||
'FirestoreSettings.host cannot be falsey' | |||
); | |||
|
|||
if (!this._config.settings.timestampsInSnapshotsEnabled) { | |||
log.error(` | |||
The default behavior for Date objects stored in Firestore is going to change |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed the tests, including one test for server timestamps that previously checked for Timestamps if the setting is true, but now checks for Dates if setting is false.
packages/firestore-types/index.d.ts
Outdated
@@ -20,21 +20,45 @@ import { FirebaseApp, FirebaseNamespace } from '@firebase/app-types'; | |||
* Document data (for use with `DocumentReference.set()`) consists of fields | |||
* mapped to values. | |||
*/ | |||
export type DocumentData = { [field: string]: any }; | |||
export type DocumentData = { | |||
[field: string]: any; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, this sounds like it could be the cause. I first formatted using clang-format
, which resulted in a pretty different formatting, and I guess prettier
couldn't restore it successfully.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test changes look good. Thanks! Also, I forgot to reply to your comment about dist/ but you shouldn't ever need to check in anything there. That's all build output, and shouldn't be in git AFAIK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mikelehen Michael, sorry, I have a couple more questions:
- I just noticed that the automated "prettier" commit added a huge package-lock.json file. I'm pretty sure this has happened before, but I didn't notice and didn't remove it this time. Is this a known issue?
- since in this PR, we decided to configure tests to use the new behavior, do you think other platforms should be updated to do the same?
Hrm. My guess is you accidentally created the package-lock.json file while the prepush hook was running and it looks like (
I'm not 100% sure what you would have done to create package-lock.json though... perhaps you did an "npm" command instead of "yarn" or something? FYI- I think I see an unintentional files.txt in your PR too... As for the test configuration change, yes please! Other than things that are actually platform-specific we want to keep the platforms as in-sync as possible. It's a bit painful but it makes for healthy and consistent codebases. So the effort is appreciated. :-) |
- make _areTimestampsInSnapshotsEnabled a regular function instead of a property (underscore in the name doesn't work well with the minifier); - remove type annotations from code snippets in the log warning, and change reference to `app` (presumably a variable defined previously) to `firebase.app()`; - undo most of formatting changes (the exception is breaking lines above the column limit); - restore alphabetical order of imports.
…havior will not be retained)
@mikelehen Michael, could you please take another look? The only change since the last time is slight tweaking of warning due to feedback on the doc. The idea was to phrase it clearer that the current behavior is not going to be supported in the future. I'll be happy to change if you'd like. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with minor nit and unfortunately we need to update a couple more index.d.ts files. :-/
@@ -35,6 +35,26 @@ export interface Settings { | |||
host?: string; | |||
/** Whether to use SSL when connecting. */ | |||
ssl?: boolean; | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So... it turns out our types exist in 3 .d.ts files. 😦 @jshcrowthe has plans to fix this, but for now can you update packages/firebase/index.d.ts and packages/firebase/app/index.d.ts too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the tip!
To hide this warning and ensure your app does not break, you need to add the | ||
following code to your app before calling any other Cloud Firestore methods: | ||
|
||
const firestore = firebase.firestore(firebase.app()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think firebase.app()
is not necessary? Just doing firebase.firestore()
should use the default app...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Just out of curiosity, where is the code handling this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So it's a bit convoluted. firebase.firestore() is actually a function that FirebaseApp creates when we (Firestore) call firebase.INTERNAL.registerService() to register ourselves. So:
- We call firebase.INTERNAL.registerService() here:
(firebase as _FirebaseNamespace).INTERNAL.registerService( - It creates this
serviceNamespace
which is the firebase.firestore() function [see line 369] except it also has properties like firebase.firestore.FieldValue hence it's called a "namespace":const serviceNamespace = (appArg: FirebaseApp = app()) => { - That serviceNamespace function defaults to using the default app (
appArg: FirebaseApp = app()
).
So app is optional...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I tried finding it in the code, realized it's something dynamically created and gave up.
…face is contained
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still LGTM. Tiny optional suggestion for changelog.
packages/firestore/CHANGELOG.md
Outdated
@@ -2,6 +2,14 @@ | |||
- [fixed] Fixed a regression in the Firebase JS release 4.11.0 that could | |||
cause get() requests made while offline to be delayed by up to 10 | |||
seconds (rather than returning from cache immediately). | |||
- [feature] Added a new `Timestamp` class to represent timestamp fields, | |||
currently supporting up to microsecond precision. It can be passed to API | |||
methods anywhere a system Date is currently accepted. To make |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
optional nit: 'JS Date object' might be clearer than 'system Date' (here and below)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Non package specific changes LGTM 👍
No description provided.