Byron / google-apis-rs

A binding and CLI generator for all Google APIs
http://byron.github.io/google-apis-rs
Other
982 stars 132 forks source link

[drive3] "there is no reactor running, must be called from the context of a Tokio 1.x runtime" #290

Open virtualritz opened 3 years ago

virtualritz commented 3 years ago

I get:

thread 'main' panicked at 'there is no reactor running, must be called from the context of a Tokio 1.x runtime', /Users/moritz/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.8.1/src/runtime/context.rs:18:26
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

I tried just wrapping a main around the example from the docs:

 use google_drive3 as drive3;
 use hyper;
 use hyper_rustls;
 use yup_oauth2 as oauth2;
 use drive3::DriveHub;
 use drive3::Error;
 use std::default::Default;
 use futures::executor::block_on;

 fn main() {
     let future = gdrive();
     block_on(future);
 }

 async fn gdrive() {
     // Get an ApplicationSecret instance by some means. It contains the `client_id` and
     // `client_secret`, among other things.
     let secret: oauth2::ApplicationSecret = Default::default();
     // Instantiate the authenticator. It will choose a suitable authentication flow for you,
     // unless you replace  `None` with the desired Flow.
     // Provide your own `AuthenticatorDelegate` to adjust the way it operates and get feedback about
     // what's going on. You probably want to bring in your own `TokenStorage` to persist tokens and
     // retrieve them from storage.
     let auth = yup_oauth2::InstalledFlowAuthenticator::builder(
         secret,
         yup_oauth2::InstalledFlowReturnMethod::HTTPRedirect,
     )
     .build()
     .await
     .unwrap();
     let hub = DriveHub::new(
         hyper::Client::builder().build(hyper_rustls::HttpsConnector::with_native_roots()),
         auth,
     );
     // You can configure optional parameters by calling the respective setters at will, and
     // execute the final call using `doit()`.
     // Values shown here are possibly random and not representative !
     let result = hub
         .files()
         .list()
         .team_drive_id("invidunt")
         .supports_team_drives(true)
         .supports_all_drives(true)
         .spaces("sed")
         .q("ut")
         .page_token("gubergren")
         .page_size(-16)
         .order_by("est")
         .include_team_drive_items(true)
         .include_permissions_for_view("ipsum")
         .include_items_from_all_drives(true)
         .drive_id("ea")
         .corpus("dolor")
         .corpora("Lorem")
         .doit()
         .await;

     match result {
         Err(e) => match e {
             // The Error enum provides details about what exactly happened.
             // You can also just use its `Debug`, `Display` or `Error` traits
             Error::HttpError(_)
             | Error::Io(_)
             | Error::MissingAPIKey
             | Error::MissingToken(_)
             | Error::Cancelled
             | Error::UploadSizeLimitExceeded(_, _)
             | Error::Failure(_)
             | Error::BadRequest(_)
             | Error::FieldClash(_)
             | Error::JsonDecodeError(_, _) => println!("{}", e),
         },
         Ok(res) => println!("Success: {:?}", res),
     }
 }
Byron commented 3 years ago

Thanks for the hint! That's probably an oversight.

Were you able to verify that adding the tokio runtime via derive (for example like #[tokio::runtime] fn main()…) works as expected?

If so, it should be trivial to add this part to the generated code blocks

virtualritz commented 2 years ago

So after fixing this (for others running into this, tokio needs the full feature enabled or the main macro to work), I get it to run and print:

Please direct your browser to ?scope=https://www.googleapis.com/auth/drive.metadata.readonly&access_type=offline&redirect_uri=http://127.0.0.1:65018&response_type=code&client_id= and follow the instructions displayed there.

Opening that link in Chrome gives me:

You are receiving this error either because your input OAuth2 scope name is invalid or it refers to a newer scope that is outside the domain of this legacy API.

This API was built at a time when the scope name format was not yet standardized. This is no longer the case and all valid scope names (both old and new) are catalogued at https://developers.google.com/identity/protocols/oauth2/scopes. Use that webpage to lookup (manually) the scope name associated with the API you are trying to call and use it to craft your OAuth2 request.

Aka: no cigar. :/

virtualritz commented 2 years ago

For reference, my code now looks like this:

use google_drive3 as drive3;
use hyper;
use hyper_rustls;
use yup_oauth2 as oauth2;
use drive3::DriveHub;
use drive3::Error;
use std::default::Default;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let secret: oauth2::ApplicationSecret = Default::default();

    let auth = yup_oauth2::InstalledFlowAuthenticator::builder(
        secret,
        yup_oauth2::InstalledFlowReturnMethod::HTTPRedirect,
    )
    .build()
    .await
    .unwrap();

    let hub = DriveHub::new(
        hyper::Client::builder().build(hyper_rustls::HttpsConnector::with_native_roots()),
        auth,
    );

    let result = hub
        .files()
        .list()
        .team_drive_id("invidunt")
        .supports_team_drives(true)
        .supports_all_drives(true)
        .spaces("sed")
        .q("ut")
        .page_token("gubergren")
        .page_size(-16)
        .order_by("est")
        .include_team_drive_items(true)
        .include_permissions_for_view("ipsum")
        .include_items_from_all_drives(true)
        .drive_id("ea")
        .corpus("dolor")
        .corpora("Lorem")
        .doit()
        .await;

    match result {
        Err(e) => match e {
            // The Error enum provides details about what exactly happened.
            // You can also just use its `Debug`, `Display` or `Error` traits
            Error::HttpError(_)
            | Error::Io(_)
            | Error::MissingAPIKey
            | Error::MissingToken(_)
            | Error::Cancelled
            | Error::UploadSizeLimitExceeded(_, _)
            | Error::Failure(_)
            | Error::BadRequest(_)
            | Error::FieldClash(_)
            | Error::JsonDecodeError(_, _) => println!("{}", e),
        },
        Ok(res) => println!("Success: {:?}", res),
    }

    Ok(())
}
Byron commented 2 years ago

Thanks a lot, this will help!

Regarding authentication, that's probably a bit under-documented in the documentation right now. I would expect that the yup-oauth2 docs should more helpful to get this to work. In short, one would have to setup an application and respective API assignments in the google dev console to get a JSON secret for use in the application. The default one it generates was one linked to my account but it expired long ago.

k86td commented 7 months ago

Came across this and just wanted to clarify for people seeing this in the future. From my testing (as of 5.0.3), I was able to make the OAuth2 request go through by specifying the fields:

    secret.client_id = "client_id".to_string();
    secret.client_secret = "client_secret".to_string();
    secret.project_id = Some("project_id".to_string());
    secret.auth_uri = "https://accounts.google.com/o/oauth2/auth".to_string();
    secret.token_uri = "https://oauth2.googleapis.com/token".to_string();
    secret.auth_provider_x509_cert_url =
        Some("https://www.googleapis.com/oauth2/v1/certs".to_string());

The fields can be found in the token file generated preliminary :) Reference