Page MenuHomePhabricator

[Identity] DatabaseClient method to get user token
ClosedPublic

Authored by varun on May 6 2022, 8:33 AM.
Tags
None
Referenced Files
F1774066: D3957.diff
Thu, May 16, 12:29 AM
Unknown Object (File)
Wed, May 1, 3:41 PM
Unknown Object (File)
Tue, Apr 23, 8:00 PM
Unknown Object (File)
Tue, Apr 23, 8:00 PM
Unknown Object (File)
Tue, Apr 23, 8:00 PM
Unknown Object (File)
Tue, Apr 23, 8:00 PM
Unknown Object (File)
Tue, Apr 23, 8:00 PM
Unknown Object (File)
Tue, Apr 23, 8:00 PM

Details

Summary

Adding a method to fetch a user's token from DynamoDB. The client will attempt to contruct and return a Token object from the DynamoDB item.

Test Plan

Successfully fetched tokens from DynamoDB with complete and incomplete data

Diff Detail

Repository
rCOMM Comm
Lint
Lint Not Applicable
Unit
Tests Not Applicable

Event Timeline

varun requested review of this revision.May 6 2022, 8:38 AM

Use AuthType and Token from token module

services/identity/src/database.rs
118–180 ↗(On Diff #12375)

Man, this gets really deeply nested... I know Rust makes this hard, but is there any way to reduce some of the nesting?

karol added inline comments.
services/identity/src/database.rs
91 ↗(On Diff #12375)

Wouldn't it be more readable to explicitly specify the collection's generic args?

91–106 ↗(On Diff #12375)

Maybe it's worth mentioning which one of these is a partition key? I know it's intuitive to treat the first one in such a way, but still. I don't feel strongly, it's just an idea, so deferring to you.

92–98 ↗(On Diff #12375)

This code's repeated here twice and I've also seen similar snippets around the arising(e.g. D3960) rust codebase. It looks like a boilerplate, maybe there is a way of creating some global function or something to make it shorter and avoid such explicit boilerplate?

119–136 ↗(On Diff #12375)

I may be wrong but I have a feeling that I've seen this as well. Unpacking seems to also carry a burden of boilerplate. Isn't there a way of handling this using a shorter code? Or could we maybe create a set of tools for this (some global functions or something)? This code is rather simple but repetitive and long.

162 ↗(On Diff #12375)

What exactly does error!(...) do? Will it break the execution here or are we going to return Ok regardless, just with None values?

182 ↗(On Diff #12375)
  1. What exactly can be here? None? Or GetItemOutput with item: None?
  2. Maybe we should explicitly handle such a situation and leave Ok(_) for throwing a critical error of some kind of UB? What I mean is, have you considered all possible situations here? Is it possible that the program flow will enter Ok(_) even though there's a different situation than the one described in the info!(...)?
This revision now requires changes to proceed.May 9 2022, 4:31 AM
varun added inline comments.
services/identity/src/database.rs
91 ↗(On Diff #12375)

ehhh i think it's pretty clear that the Key is a String and the Value is an AttributeValue. If you feel strongly about this I can construct the HashMap a different way, but this is way I usually see it done.

91–106 ↗(On Diff #12375)

yeah a comment labeling the partition and sort keys makes sense

92–98 ↗(On Diff #12375)

yeah let me try to refactor some of this...

118–180 ↗(On Diff #12375)

I think we can reduce some of it. Will address in next revision

119–136 ↗(On Diff #12375)

I think there's some similar code, I can try to refactor this

162 ↗(On Diff #12375)

it's just a log line

182 ↗(On Diff #12375)

I think I answered a similar question in an earlier diff. We're matching Ok(GetItemOutput { item: None, .. }), and _ is just shorthand since we don't care about the contents.

Is it possible that the program flow will enter Ok(_) even though there's a different situation than the one described in the info!(...)?

No, the situation described in the info log is the only possible one that we could encounter in this arm of the match statement. The compiler also ensures that all possible outcomes are handled (will throw an error if patterns are non-exhaustive).

services/identity/src/database.rs
91–106 ↗(On Diff #12375)

decided not to include the comment after the refactor

119–136 ↗(On Diff #12375)

this actually isn't repeated anywhere. it's specific to this function

karol added inline comments.
services/identity/src/database.rs
88–93 ↗(On Diff #12582)

How about storing values like

  • "identity-tokens"
  • "userID"
  • "deviceID"

in some kind of "constants" module?

121–139 ↗(On Diff #12582)

There's still a lot of boilerplate here. This pattern repeats a lot. I looked closer at this and it indeed seems to be hard to get rid of (please note however that I barely know rust).

144 ↗(On Diff #12582)

What does * mean here?

This revision now requires changes to proceed.May 16 2022, 2:02 AM

Simplify code a little more

services/identity/src/database.rs
88–93 ↗(On Diff #12582)

I think this is a good idea but I'm going to defer it if that's alright. https://linear.app/comm/issue/ENG-1174/create-constants-module

121–139 ↗(On Diff #12582)

the pattern repeats but i don't think the logic really does.. i'm definitely trying to make this code as DRY as possible

144 ↗(On Diff #12582)

it's a dereferencing operation

Accepting, but pls respond to the doubt about dereferencing.

services/identity/src/database.rs
144 ↗(On Diff #12582)

Sorry, but I don't understand. So valid is Option<bool>, right (or not)? When I try to dereference something like this (I tried on number type), I get type Option<i32> cannot be dereferenced. Could you please describe what type is dereferenced and how it works because I seem to be missing something? Thanks.

The code seems ok, but is really hard to read. One possible solution might be to improve the formatting. Do we have some formatting rules, or are we using the default ones? Could we spend some time searching for something that is more readable?

services/identity/src/database.rs
105 ↗(On Diff #12965)

It took mi some time to notice that this is an opening bracket of let if. Shouldn't we place it at the end of the previous line?

107 ↗(On Diff #12965)

Should we use the same code format rule as in other places, e.g. 80 char line limit?

In D3957#115667, @palys-swm wrote:

The code seems ok, but is really hard to read. One possible solution might be to improve the formatting. Do we have some formatting rules, or are we using the default ones? Could we spend some time searching for something that is more readable?

Check out the rustfmt.toml file for our current formatting rules. We default to the Rust community's rules besides max_width and tab_spaces. Happy to add more rules if you think they'll improve readability. https://github.com/rust-lang/rustfmt/blob/master/Configurations.md

services/identity/src/database.rs
105 ↗(On Diff #12965)

I just use cargo fmt to format the code. I don't love how it looks but it's better than manually formatting.

107 ↗(On Diff #12965)

We have a rustfmt.toml file where we set a rule max_width = 80 but rustfmt seems to ignore log lines... tried googling but couldn't find a quick answer.

144 ↗(On Diff #12582)

valid is a &bool because we are borrowing it in the if let expression. therefore, to actually assign the value of valid to the new variable with the same name, we have to dereference it.

services/identity/src/database.rs
144 ↗(On Diff #12582)

I see, thx.

jim requested changes to this revision.EditedMay 24 2022, 12:47 PM

First, break this into subroutines for readability and reuse across DB fetchers. Like you should have functions parseTimestampAttribute, parseAuthType, etc and call parseTimestampAttribute(item.remove("created")).

Second, I don't love how it's logging all these errors and stuff here. There's already duplication in that code and you're going to get more as you add more add more DB fetchers. You should be constructing error types that can display enough info to be useful and outside of this just call error!("user {}'s token for device {} is invalid: {}, user_id, device_id, err). I would add two more types

struct DBItemError {
  attribute_errors: Vec<(&'static str, AttributeValue, DBItemAttributeError)>,
}

enum DBItemAttributeError {
  Missing,
  InvalidTimestamp(chrono::ParseError),
  InvalidAuthType,
  ...
}

And you should be able to Display DBItemError well enough.

services/identity/src/database.rs
151 ↗(On Diff #12965)

If you do item.remove("token") instead of .get you can avoid cloning the String

You should do this for all fields

197 ↗(On Diff #12965)

I would rename this to InvalidTimestamp(chrono::ParseError)

This revision now requires changes to proceed.May 24 2022, 12:47 PM

Addressed the feedback about subroutines. Improving error handling was making this diff really big, and it requires changes to other diffs in this stack, so I'm going to defer it for now if that's OK. Created this Linear issue to track: https://linear.app/comm/issue/ENG-1196/improve-error-handling-for-db-module

This revision is now accepted and ready to land.May 27 2022, 12:05 PM