-
Notifications
You must be signed in to change notification settings - Fork 937
Remove Cyclic Dependencies #4160
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
Conversation
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.
Very impressive!
One high level concern I have with the PR as is, is that it's not necessarily obvious where to look for the implementation of a given type. For example, in some cases it's the _impl but in others it's _methods. Also, whether or not this split even exists seems arbitrary.
It also seems that if you squint just right, this is a very similar change to the forward declarations change we made in iOS C++: declare the exported types separately from the actual implementations to drastically reduce the number of files imported by those type declarations. In the iOS implementation, we created a single file that "exported" the types for a package, e.g. model in [model_fwd.h](https://github.com/firebase/firebase-ios-sdk/blob/master/Firestore/core/src/model/model_fwd.h)
. I wonder if a similar approach here could make this feel more regular.
* If foo is not an object, foo is replaced with an object | ||
* containing foo | ||
*/ | ||
export class FieldMask { |
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.
FieldMask is in a separate file on other platforms. In some cases (e.g database.ts) I know circularity is impossible to avoid and so we have to dump them all in a single file, but elsewhere keeping classes separate and matching structure across platforms is helpful.
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. Works without any adjustments.
* limitations under the License. | ||
*/ | ||
|
||
export const enum TypeOrder { |
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.
Now that this is in a separate file, maybe give it a comment?
https://github.com/firebase/firebase-ios-sdk/blob/master/Firestore/core/src/model/field_value.h#L62 would be suitable.
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. I actually added a small comment in a later commit, but the one you provided is better.
@@ -196,3 +194,436 @@ export function isDocumentTarget(target: Target): boolean { | |||
target.filters.length === 0 | |||
); | |||
} | |||
|
|||
export abstract class Filter { |
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.
Now that everything pertaining to queries isn't in a single file, it seems like we could split this further to make it more like the other platforms. While "target" exists as a class there, it doesn't seem like the obvious home for everything else.
Perhaps create:
filter.ts
forFilter
,FieldFilter
, the various Filter implementations, and canonify/stringify opsorder_by.ts
forDirection
,OrderBy
, and canonify/stringify opsbound.ts
forBound
,sortsBeforeDocument
, and others
?
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.
Will address in follow up.
@@ -0,0 +1,61 @@ | |||
/** |
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.
"value serializer" seems like it could include anything we might think to put in the value proto. Would it make sense to move things like Timestamp, bytes, and GeoPoint serialization into here too?
This also matches the trend on the other platforms to split value proto handling into a separate file 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.
The serialization of these types actually happens in UserDataReader. The Bytes/Timestamp methods in serializer are only used as helpers for the Watch protos (among other things).
I renamed the file to number_serializer for now.
@@ -0,0 +1,40 @@ | |||
/** |
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.
PersistenceTransactionMode
seems like something you might look for here too.
Edit: it seems like this was addressed in a later commit.
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.
Yes. Done.
* Sentinel values that can be used when writing document fields with `set()` | ||
* or `update()`. | ||
*/ | ||
export abstract class FieldValue { |
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 can't say I really understand the layering model we have here and why this move is desirable.
It seems like conceptually the classic API should only ever point to the lite API and not the other way around. Allowing the other order encourages cycles and the possibility of polluting the lite implementation with classes that can't be tree shaken.
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, this is also something that was fixed in a later commit. It is indeed in lite/src/api/field_value
@@ -398,6 +405,148 @@ export function parseSetData( | |||
); | |||
} | |||
|
|||
export class DeleteFieldValueImpl extends FieldValue { |
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.
A possible thought to follow up on layering: would it make sense to explicitly break out common code and the classic API as separate paths? That way you could impose layering checks, e.g.
- lite -> common
- exp -> (lite, common)
- classic -> (exp, common)
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.
Once all the dust has settled, I would like to move the /exp and /lite files under /src. They only contain API code and the nested imports are not pretty. This is something I will consider then.
// The components module manages the lifetime of dependencies of the Firestore | ||
// client. Dependencies can be lazily constructed and only one exists per | ||
// Firestore instance. | ||
|
||
export interface FirestoreService extends _FirebaseService { |
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.
Comments, please. In particular what's a FirestoreService and how does it differ from a FirebaseFirestore instance?
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 added the following:
/**
* An interface implemented by FirebaseFirestore that provides
* compatibility with the usage in this file.
*
* This interface mainly exists to remove a cyclic dependency.
*/
I will explore doing the same FirebaseFirestore/FirebaseFirestoreImpl pattern in a follow-up PR.
options: SetOptions | ||
): Promise<void>; | ||
|
||
export function setDoc<T>( |
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.
Setting, updating, and adding documents doesn't seem to have anything to do with "queries". Did you mean to move this here given that reference.ts
still exists?
Alternatively, if this can't exist in reference.ts
to solve circularity issues, maybe create reference_impl.ts
to match the structure you've established elsewhere (e.g. with LocalStoreImpl)?
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, this was also cleaned up in a later commit, which I know is impossible to detect with a PR this size.
I did rename the file to reference_impl.ts 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.
Feedback addressed (or punted to follow up work for next week). Sorry about this PR and thanks for the review!
@@ -196,3 +194,436 @@ export function isDocumentTarget(target: Target): boolean { | |||
target.filters.length === 0 | |||
); | |||
} | |||
|
|||
export abstract class Filter { |
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.
Will address in follow up.
* limitations under the License. | ||
*/ | ||
|
||
export const enum TypeOrder { |
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. I actually added a small comment in a later commit, but the one you provided is better.
* If foo is not an object, foo is replaced with an object | ||
* containing foo | ||
*/ | ||
export class FieldMask { |
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. Works without any adjustments.
@@ -0,0 +1,61 @@ | |||
/** |
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.
The serialization of these types actually happens in UserDataReader. The Bytes/Timestamp methods in serializer are only used as helpers for the Watch protos (among other things).
I renamed the file to number_serializer for now.
@@ -0,0 +1,40 @@ | |||
/** |
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.
Yes. Done.
packages/firestore/src/model/path.ts
Outdated
@@ -366,3 +366,57 @@ export class FieldPath extends BasePath<FieldPath> { | |||
return new FieldPath([]); | |||
} | |||
} | |||
|
|||
export class DocumentKey { |
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 swear it did when I looked at it, but it seems to have magically disappeared. Reverted this change.
* Sentinel values that can be used when writing document fields with `set()` | ||
* or `update()`. | ||
*/ | ||
export abstract class FieldValue { |
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, this is also something that was fixed in a later commit. It is indeed in lite/src/api/field_value
@@ -398,6 +405,148 @@ export function parseSetData( | |||
); | |||
} | |||
|
|||
export class DeleteFieldValueImpl extends FieldValue { |
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.
Once all the dust has settled, I would like to move the /exp and /lite files under /src. They only contain API code and the nested imports are not pretty. This is something I will consider then.
// The components module manages the lifetime of dependencies of the Firestore | ||
// client. Dependencies can be lazily constructed and only one exists per | ||
// Firestore instance. | ||
|
||
export interface FirestoreService extends _FirebaseService { |
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 added the following:
/**
* An interface implemented by FirebaseFirestore that provides
* compatibility with the usage in this file.
*
* This interface mainly exists to remove a cyclic dependency.
*/
I will explore doing the same FirebaseFirestore/FirebaseFirestoreImpl pattern in a follow-up PR.
options: SetOptions | ||
): Promise<void>; | ||
|
||
export function setDoc<T>( |
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, this was also cleaned up in a later commit, which I know is impossible to detect with a PR this size.
I did rename the file to reference_impl.ts as well.
@@ -2560,14 +2560,12 @@ | |||
); | |||
case 1: | |||
return ( | |||
e |
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.
FYI: This is removed in a commit later.
@@ -68,7 +69,7 @@ export const CACHE_SIZE_UNLIMITED = LRU_COLLECTION_DISABLED; | |||
* Do not call this constructor directly. Instead, use {@link getFirestore}. | |||
*/ | |||
export class FirebaseFirestore extends LiteFirestore { | |||
readonly _queue = newAsyncQueue(); | |||
readonly _queue: AsyncQueue = newAsyncQueue(); |
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.
Somehow the AsyncQueue
type is required to not break the APIExtractor toolchain.
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
@@ -1,6 +1,6 @@ | |||
/** | |||
* @license | |||
* Copyright 2017 Google LLC | |||
* Copyright 2020 Google LLC |
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.
If you change this back to 2017 and add a newline back after import { debugAssert }
this file we be unchanged in 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.
Done
@@ -1,6 +1,6 @@ | |||
/** | |||
* @license | |||
* Copyright 2017 Google LLC | |||
* Copyright 2020 Google LLC |
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.
If you change this back to 2017 and revert the import reordering, this file could now be unchanged in 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.
Done
This PR should unblock the Console build and should also allow us to revive our old goal to sort our imports.
It also adds a build rule change to block us from introducing circular dependencies in the future (ec5ac56)