-
-
Notifications
You must be signed in to change notification settings - Fork 41
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 the public API more modular #49
Conversation
Given that this library makes use of async code, it's useful to use spans.
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 new API to let you create structs directly feels natural, though it is weird to single out the CustomServiceAccount as the only public one. Maybe all should be made public? That way someone could also say use only the DefaultServiceAccount or something like that.
src/authentication_manager.rs
Outdated
@@ -21,10 +25,66 @@ pub struct AuthenticationManager { | |||
} | |||
|
|||
impl AuthenticationManager { | |||
pub(crate) fn new(client: HyperClient, service_account: Box<dyn ServiceAccount>) -> Self { | |||
/// Create an `AuthenticationManager` directly from a custom service account | |||
pub fn from_custom_service_account(service_account: CustomServiceAccount) -> Self { |
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.
is it customary to provide this as well as a From
impl? Not that I have any problem with this, just wondering if there is a Rust-wide consensus 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, I was maybe overdoing it on the ergonomics. I've found that often From
implementations aren't very easily discoverable, and in my mind gcp_auth is about making this complicated stuff really easy; but maybe we should double down on There's Only One Way To Do It and we should ditch this method in favor of the From
impl.
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 discoverability is fair, but easily solvable by showing it in the struct docs, maybe even with an example.
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.
That's fair. I'm interested what @hrvolapeter thinks.
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 both arguments valid. I was trying to find if some other library is having from trait and function as well but wasn't able to find anything. Adding public function is easier then removing it so I would probably just start with From trait and if people are confused we can consider improving docs / making function explicit
src/lib.rs
Outdated
/// Initialize GCP authentication | ||
/// | ||
/// Returns `AuthenticationManager` which can be used to obtain tokens | ||
/// This is a convenience wrapper around [`AuthenticationManager::new()`]. |
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 don't mind breaking the API why not completely remove this init and rely only on AuthenticationManager::new
?
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 personally like the shortcut of being able to do gcp_auth::init()
without doing any imports, especially in helper CLIs. Seems like a cheap way to get a slicely nicer UX.
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 have no strong opinions and recognise this is entirely bikeshedding, so if you like to keep both that's perfectly fine. Though in rust I did get very used to the SomeStruct::constructor()
way over an init shortcut and when I have the option (I think tracing_subscriber::fmt() is another example) I will still choose the constructor with the struct name in it as it is more descriptive to me to see the struct name being created.
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 data point! I think I rather like the import-less initialization aspect to me but I will recognize it's less idiomatic then calling a type constructor.
Again, since @hrvolapeter came up with this API initially I'll let them weigh in before I make any further changes.
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 whole init think is probably just a habit from university, we've been taught that constructor shouldn't have side effects due to weird error handling in C++ constructors which is not really a valid point in rust anymore. init
for me is basically constructor with side effects, I think similar approach is quite common in OOP although it would be constructed in 2 steps. I agree that it's probably not the most rust native way.
} | ||
|
||
/// The project ID as found in the credentials | ||
pub fn project_id(&self) -> Option<&str> { |
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 seems odd to me to provide two public functions with the same name on the same struct. I realise this is a slightly different signature that the ServiceAccount
trait, but this seems hardly worth 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.
The ServiceAccount
trait is pub(crate)
, so that implementation is actually not public.
tokens: RwLock::new(HashMap::new()), | ||
}) | ||
} | ||
|
||
/// The RSA PKCS1 SHA256 [`Signer`] used to sign JWT tokens | ||
pub fn signer(&self) -> &Signer { |
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.
why make the signer public?
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 there's a use case here: ThouCheese/cloud-storage-rs#92. This is for the https://cloud.google.com/storage/docs/access-control/signed-urls feature, which uses the same encryption mechanism as far as I can tell.
More broadly, this is basically code that we already have in gcp_auth, so we might as well expose it to users as an easy API -- they might also want to produce JWTs.
We could definitely do the others. Personally I find the |
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 proposed changes look good. All open discussions seems reasonable in both ways
I removed |
Yes please do |
This does way with all top-level functions except
init()
, while makingAuthenticationManager::new()
(effectively the same asinit()
) and theCustomServiceAccount
type public. The latter can be constructed directly withfrom_env()
,from_file()
andfrom_json()
callers, and can easily be converted to anAuthenticationManager
. It also gives access to some of its inner credentials which I think will be useful.I've also switch the logging provider from log to tracing and gave
CustomServiceAccount
callers access to its signer.@flub any thoughts?