-
Notifications
You must be signed in to change notification settings - Fork 124
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
[WIP] Auth restructure #207
Conversation
Ok so turns out this trait-based implementation is only possible if we use This means that we are forced to use We already do something similar for every endpoint anyway because of This whole thing could also be fixed with delegation instead of a trait system, but that's not happening anytime soon, so I would keep going with traits for now: rust-lang/rfcs#2393 (comment). |
Unfortunately the diff is going to be a mess because I'm splitting |
So I've got a couple examples working now with the new interface. You can take a look and let me know your thoughts/suggestions:
Actually all of them work now, except for the PKCE one which is still WIP. But I might leave the PKCE implementation for another PR to avoid changing too many things in this huge one. |
@@ -25,19 +26,23 @@ resolver = "2" | |||
[dependencies] | |||
rspotify-macros = { path = "rspotify-macros", version = "0.10.0" } | |||
rspotify-model = { path = "rspotify-model", version = "0.10.0" } | |||
rspotify-http = { path = "rspotify-http", version = "0.10.0", default-features = false } |
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 doesn't rspotify-http
enable by default?
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.
Because we need to manually specify which client to use. Perhaps rspotify-http
shouldn't enable clients by default, and that should be done by rspotify
itself?
|
||
self.token = Some(self.fetch_access_token(&data).await?); | ||
|
||
self.write_token_cache() |
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.
Should we implement cache token
feature in this PR or leave a TODO item?
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 did implement cached tokens in this PR, but I've left refreshing ones for another. But you can just ignore them for now and review it later on.
|
||
/// Obtains the client access token for the app. The resulting token will be | ||
/// saved internally. | ||
#[maybe_async] |
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 resulting token will be saved internally if the cache-token
feature is 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.
Yes. This function calls write_token_cache
at the end so it will be written (only if cached tokens are enabled, as you may see in said function's definition)
src/code_auth.rs
Outdated
let token = self.fetch_access_token(&data).await?; | ||
self.token = Some(token); | ||
|
||
self.write_token_cache() |
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.
Same as above
src/code_auth.rs
Outdated
/// | ||
/// [reference]: https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow | ||
/// [example-main]: https://github.com/ramsayleung/rspotify/blob/master/examples/code_auth.rs | ||
/// [example-webapp]: https://github.com/ramsayleung/rspotify/tree/master/examples/webapp |
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 it will be better if we link reference with relative paths instead of HTTP links, which will be easily broken if I change username or repository
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 can link to the examples with relative paths, as they aren't uploaded to docs.rs
(AFAIK). Or maybe I've misunderstood you?
src/code_auth.rs
Outdated
} | ||
|
||
/// This client has access to the base methods. | ||
#[maybe_async(?Send)] |
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 would we need the Send
trait?
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.
This will probably explain it better than I can: https://docs.rs/async-trait/0.1.50/async_trait/#non-threadsafe-futures. I basically had to add + Send
to each generic type in the trait functions, so passing (?Send)
to the macro does that automatically.
src/endpoints/oauth.rs
Outdated
/// | ||
/// Note: this method requires the `cli` feature. | ||
#[cfg(feature = "cli")] | ||
fn get_code_from_user(&self, url: &str) -> ClientResult<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.
Not sure it's suitable to put get_code_from_user
into OAuthClient
trait, since we could get code without token
and cred
parameters. Same as parse_response_code
.
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.
But get_code_from_user
doesn't really make sense if you're not using oauth, right? You need the code for the authorization flows, which is not needed in a base client.
src/endpoints/pagination/iter.rs
Outdated
|
||
/// Alias for `Iterator<Item = T>`, since sync mode is enabled. | ||
pub trait Paginator<T>: Iterator<Item = T> {} | ||
impl<T, I: Iterator<Item = T>> Paginator<T> for I {} | ||
pub type Paginator<'a, T> = Box<dyn Iterator<Item = T> + 'a>; |
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 totally get it now why would we need to declare type pub type Paginator<'a, T> = Box<dyn Iterator<Item = T> + 'a>;
instead of the previous one ?
pub trait Paginator<T>: Iterator<Item = T> {}
impl<T, I: Iterator<Item = T>> Paginator<T> for I {}
Because we need some kinds of dynamic dispatch?
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 trait-based implementation of these pagination methods is only possible if we use -> Box<dyn Paginator>
instead of -> impl Paginator
. The compiler will complain about it the same way we can't have -> impl Future<...>
in traits, which we fix with async_trait
(a macro that basically adds the Box
automatically) . This is technically temporary and will be fixed in future Rust versions, but I'd give it at least a year until that happens, really. So we are basically forced to use Box + dyn
if we want it to work for now.
This is sub-optimal because there's a slight runtime overhead in comparison to generics, but on the other hand there should be less code bloat due to generics (not that either of these really matter, IMO).
Sorry for converting it from Draft to PR, my bad :( What a huge PR, it takes me hours to give a rough review. The whole architecture of Auth process looks much clear and understandable than before, the current architecture looks good to me. Perhaps you could add some diagrams about the current Auth process.
A big yes for all this suggestions. By the ways, would you like splitting this PR into a Auth Rewrite PR and a Separate Crate PR, this PR has too many changes that it's a little hard to review line by line :) |
261cd9d
to
8c016bc
Compare
Yes, it's a complete rewrite so the diff is just very hard to follow. I did want to add some diagrams but I wanted to make sure you were ok with it first of all. Here's a quick summary: we now have the implementations in traits: This has some advantages and some inconvenients:
If you have any doubts do let me know. I know it's hard to follow the first time after a rewrite.
How important is this? It would take me a bit of work to undo some changes and make two PRs instead. Which is not a problem, but as I said, I'd need a few more days until I'm done (and I'll be busier soon due to finals, so it might take more). Just letting you know, I understand that this PR is huge. |
IMHO, moving methods like
It doesn't really matter, it's low priority. |
How so? You only have to prompt for the user's authorization with the auth flows. Or am I missing something? I don't see why you'd use any of these in the client credentials flow. |
Oooh, I misunderstand something, you are right. |
Hello! I've finally finished my finals, so I now have time to finish this. I've recreated the PR by splitting it up into four smaller PRs. The third one is still huge, but I don't think I can do anything about it; it separates the client into multiple ones, so the diff gets messed up. As I mentioned there, the endpoints themselves aren't changed, so you can ignore them. If this still isn't digestible enough please let me know, I don't want to kill you with my reviews, @ramsayleung haha. I'll close this PR for now. I suggest you to start with the first PR up until the fourth. Afterwards we can merge all of them and push into master. But absolutely no rush. Thanks a lot for the patience! |
The good news is that there is a lot of reviews here, but it doesn't kill me. The bad news is it almost scares me out, haha :) |
TODO:
derive_builder
completely from the library.Config
struct with things liketoken_cached
ortoken_refreshing
for Consider making the cache file a feature #135. The former is implemented here, but the latter is left as a TODO.album.rs
intoclient_credentials.rs
, teaching about the authentication process instead.rspotify-http
Note that this only really separates the
rspotify-http
crate, as I don't think moving the auth logic to a separate crate is really worth it. We can discuss it but I'm not sure it's necessary at all.Closes #207
Closes #208