Skip to content

Change ProtoByteString to use its own class rather than String | Uint8Array #2639

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

Merged
merged 14 commits into from
Feb 20, 2020

Conversation

thebrianchen
Copy link

@thebrianchen thebrianchen commented Feb 15, 2020

We've been storing tokens internally as ProtoByteString, which is typed to String | Uint8Array in order to account for the fact that protos are sent as base64 strings and Uint8Arrays. However, casting between these 2 types is confusing and bug-prone, resulting in a latent bug where we were improperly decoding Uint8Arrays as storing them as strings. This PR separates creates a separate ProtoByteString class to provide a type-safe representation for tokens.

@thebrianchen thebrianchen changed the title Store Tokens as Blob rather than String | Uint8Array Change ProtoByteString to use Blob rather than String | Uint8Array Feb 15, 2020
@wilhuff wilhuff assigned schmidt-sebastian and unassigned wilhuff Feb 18, 2020
@wilhuff
Copy link
Contributor

wilhuff commented Feb 18, 2020

Took a first pass.

@schmidt-sebastian See if you disagree with any of the feedback here. On Android we have a distinction between a ByteString, which is an internal type representing the bytes (supplied by the protobuf library), and a Blob, which is the public type for storing values in Firestore. There's a similar though more complicated distinction on iOS.

I'm a little wary of using the public type internally to represent what is logically a separate ByteString type but size concerns make me wary of recommending the extra type. WDYT?

@schmidt-sebastian
Copy link
Contributor

I'm a little wary of using the public type internally to represent what is logically a separate ByteString type but size concerns make me wary of recommending the extra type. WDYT?

Blob is a pretty thin wrapper on top of the method exposed by Platform. We could address your concern by directly invoking the Platform API and representing Blobs internally as Strings. If we do that, I would advocate to still use the ProtoByteString type to keep some level of distinction (similar to what we do with TargetId).

Copy link
Author

@thebrianchen thebrianchen left a comment

Choose a reason for hiding this comment

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

After talking with Sebastian, I ended up keeping the ProtoByteString name and creating a thin class for it that mirrors Blob without the additional validation. The main issue I have with representing tokens as strings internally is that it is not type safe enough and makes it easy to introduce more bugs like this original one. We would have to remember to cast the string to each time we want to use it as a proto, even though type checking will allow us to pass the incorrect string representation.

By storing the tokens in an intermediate class, we are forced to convert the string to the correct platform representation in order to get the code to compile. Creating a separate ProtoByteString class also allows us to avoid using the public Blob type.

@thebrianchen thebrianchen changed the title Change ProtoByteString to use Blob rather than String | Uint8Array Change ProtoByteString to use its own class rather than String | Uint8Array Feb 19, 2020
@wilhuff
Copy link
Contributor

wilhuff commented Feb 19, 2020

I’m definitely in favor of a separate type.

At this point though it’s not really tied to protos so much though. Previously the union type really represented “types that protos will represent byte sequences as”. Now this is independent of that representation. Is suggest renaming to just ByteString. Not only is this internally more clearly separate from protos, but also would match iOS and Android, where we also have a ByteString type.

expect(actualTarget.resumeToken).to.equal(expectedTarget.resumeToken);
// actualTarget's resumeToken is a hexadecimal string, but the serialized
// resumeToken will be a base64 string, so we need to convert it back.
expect(actualTarget.resumeToken || '').to.equal(
Copy link
Contributor

Choose a reason for hiding this comment

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

See my comment below - can you verify that we are indeed using hexadecimal strings?

Copy link
Author

Choose a reason for hiding this comment

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

We are using hexadecimal strings here because the spec tests pass in resume tokens as standard javascript hex strings. I guess what ends up happening is that btoa() still serializes and deserializes hex strings fine, which serves the purposes of the test.

Copy link
Contributor

Choose a reason for hiding this comment

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

As discussed offline, these are all base64 encoded strings. Thanks for updating the comment.

expect(actualTarget.resumeToken).to.equal(expectedTarget.resumeToken);
// actualTarget's resumeToken is a hexadecimal string, but the serialized
// resumeToken will be a base64 string, so we need to convert it back.
expect(actualTarget.resumeToken || '').to.equal(
Copy link
Contributor

Choose a reason for hiding this comment

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

As discussed offline, these are all base64 encoded strings. Thanks for updating the comment.

@thebrianchen thebrianchen dismissed wilhuff’s stale review February 20, 2020 17:52

lgtm from sebastian

@thebrianchen thebrianchen merged commit 2919c0a into master Feb 20, 2020
@thebrianchen thebrianchen deleted the bc/blobb branch February 20, 2020 17:53
@firebase firebase locked and limited conversation to collaborators Mar 22, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants