Skip to content

Commit 010be6a

Browse files
fix: update qcs-api-client dependencies to 0.12.12, (0.13.13 grpc) (#570)
1 parent 888056b commit 010be6a

File tree

13 files changed

+1234
-208
lines changed

13 files changed

+1234
-208
lines changed

Cargo.lock

Lines changed: 1029 additions & 153 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ members = ["crates/*"]
33
resolver = "2"
44

55
[workspace.dependencies]
6-
qcs-api-client-common = "0.12.8"
7-
qcs-api-client-grpc = "0.12.8"
8-
qcs-api-client-openapi = "0.13.8"
6+
qcs-api-client-common = "0.12.12"
7+
qcs-api-client-grpc = "0.12.12"
8+
qcs-api-client-openapi = "0.13.13"
99
serde_json = "1.0.86"
1010
thiserror = "1.0.57"
1111
tokio = "1.36.0"
12+
tokio-util = "0.7"
1213
# We specify quil-rs as a git and versioned dependency so that we can keep the version of
1314
# quil-rs used in both the Rust and Python packages in sync. The tag used should always
1415
# be a `quil-py` tag and should be compatible with the version specified in

crates/lib/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ serde = { version = "1.0.145", features = ["derive"] }
4141
serde_json.workspace = true
4242
thiserror.workspace = true
4343
tokio = { workspace = true, features = ["fs", "rt-multi-thread"] }
44+
tokio-util = { workspace = true }
4445
toml = "0.7.3"
4546
tracing = { version = "0.1", optional = true, features = ["log"] }
4647
uuid = { version = "1.2.1", features = ["v4"] }
@@ -56,6 +57,9 @@ erased-serde = "0.3.23"
5657
float-cmp = "0.9.0"
5758
hex = "0.4.3"
5859
maplit = "1.0.2"
60+
# keep pinned until this crate matures
61+
oauth2-test-server = "=0.1.3"
62+
qcs-api-client-common = { workspace = true, features = ["test"] }
5963
qcs-api-client-grpc = { workspace = true, features = ["server"] }
6064
simple_logger = { version = "4.1.0", default-features = false }
6165
tempfile = "3.3.0"

crates/lib/Makefile.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ args = ["build", "--examples", "--features", "experimental"]
3535
[tasks.deny]
3636
install_crate = "cargo-deny"
3737
command = "cargo"
38-
args = ["deny", "--all-features", "check"]
38+
args = ["deny", "--exclude-dev", "--all-features", "check"]
3939

4040
[tasks.pre-ci-flow]
4141
dependencies = ["deny", "lint"]

crates/lib/src/client.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use qcs_api_client_grpc::{
1919
},
2020
};
2121
use qcs_api_client_openapi::apis::configuration::Configuration as OpenApiConfiguration;
22+
use tokio_util::sync::CancellationToken;
2223
#[cfg(not(any(feature = "grpc-web", feature = "tracing")))]
2324
use tonic::transport::Channel;
2425
use tonic::Status;
@@ -96,6 +97,27 @@ impl Qcs {
9697
ClientConfiguration::load_profile(profile).map(Self::with_config)
9798
}
9899

100+
/// Create a [`Qcs`] and initialized with the given optional `profile`.
101+
/// If credentials are not found or stale, a PKCE login redirect flow
102+
/// will be initialized. Note that this opens up a TCP port on your
103+
/// system to accept a browser HTTP redirect, so you should not use
104+
/// this in environments where that is not possible, such as hosted
105+
/// JupyterLab sessions.
106+
///
107+
/// # Errors
108+
///
109+
/// A [`LoadError`] will be returned if QCS credentials are
110+
/// not correctly configured or the given profile is not defined
111+
/// or the PKCE login flow failed.
112+
pub async fn with_login(
113+
cancel_token: CancellationToken,
114+
profile: Option<String>,
115+
) -> Result<Qcs, LoadError> {
116+
ClientConfiguration::load_with_login(cancel_token, profile)
117+
.await
118+
.map(Self::with_config)
119+
}
120+
99121
/// Return a reference to the underlying [`ClientConfiguration`] with all settings parsed and resolved from configuration sources.
100122
#[must_use]
101123
pub fn get_config(&self) -> &ClientConfiguration {

crates/lib/src/qpu/api.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ impl Eq for ExecutionOptions {}
365365
///
366366
/// Use [`Default`] to get a reasonable set of defaults, or start with [`ApiExecutionOptionsBuilder`]
367367
/// to build a custom set of options.
368-
#[derive(Builder, Clone, Debug, Default, PartialEq)]
368+
#[derive(Builder, Clone, Copy, Debug, Default, PartialEq)]
369369
#[allow(clippy::module_name_repetitions)]
370370
pub struct ApiExecutionOptions {
371371
/// the inner proto representation

crates/lib/tests/mocked_qpu.rs

Lines changed: 69 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,24 @@ async fn test_qcs_against_mocks() {
5555

5656
async fn setup() {
5757
simple_logger::init_with_env().unwrap();
58-
std::env::set_var(SETTINGS_PATH_VAR, "tests/settings.toml");
59-
std::env::set_var(SECRETS_PATH_VAR, "tests/secrets.toml");
58+
59+
// Create a temporary file to store the secrets generated by the test server,
60+
// which must be a non-expired JWT in order for the client to even attempt to make the request.
61+
let secrets_toml = tempfile::NamedTempFile::new().unwrap();
62+
63+
unsafe {
64+
std::env::set_var(SETTINGS_PATH_VAR, "tests/settings.toml");
65+
std::env::set_var(SECRETS_PATH_VAR, secrets_toml.path());
66+
}
67+
68+
let (oauth_ready_tx, oauth_ready_rx) = tokio::sync::oneshot::channel::<()>();
69+
6070
tokio::spawn(qpu::run());
6171
tokio::spawn(translation::run());
62-
tokio::spawn(auth_server::run());
72+
tokio::spawn(mock_oauth2::run(secrets_toml, oauth_ready_tx));
6373
tokio::spawn(mock_qcs::run());
74+
75+
oauth_ready_rx.await.unwrap();
6476
}
6577

6678
async fn quilc_client() -> rpcq::Client {
@@ -94,35 +106,60 @@ async fn run_bell_state(connection_strategy: ConnectionStrategy) {
94106
assert_eq!(result.duration, Some(Duration::from_micros(8675)));
95107
}
96108

97-
#[allow(dead_code)]
98-
mod auth_server {
99-
use serde::{Deserialize, Serialize};
100-
use warp::Filter;
101-
102-
#[derive(Debug, Deserialize)]
103-
struct TokenRequest {
104-
grant_type: String,
105-
client_id: String,
106-
refresh_token: String,
107-
}
108-
109-
#[derive(Serialize, Debug)]
110-
struct TokenResponse {
111-
refresh_token: &'static str,
112-
access_token: &'static str,
113-
}
114-
115-
pub(crate) async fn run() {
116-
let token = warp::post()
117-
.and(warp::path("v1").and(warp::path("token")))
118-
.and(warp::body::form())
119-
.map(|_request: TokenRequest| {
120-
warp::reply::json(&TokenResponse {
121-
refresh_token: "refreshed",
122-
access_token: "accessed",
123-
})
124-
});
125-
warp::serve(token).run(([127, 0, 0, 1], 8001)).await;
109+
mod mock_oauth2 {
110+
use std::io::Write as _;
111+
112+
use oauth2_test_server::{IssuerConfig, JwtOptions, OAuthTestServer};
113+
use tokio::task::JoinError;
114+
115+
/// A test harness for serving a valid oauth2 issuer, including the well-known endpoint.
116+
pub(super) async fn run(
117+
secrets_toml: tempfile::NamedTempFile,
118+
oauth_ready_tx: tokio::sync::oneshot::Sender<()>,
119+
) -> Result<(), JoinError> {
120+
const SCHEME: &str = "http";
121+
const HOST: &str = "127.0.0.1";
122+
const PORT: u16 = 8001;
123+
124+
let server = OAuthTestServer::start_with_config(IssuerConfig {
125+
scheme: SCHEME.to_string(),
126+
host: HOST.to_string(),
127+
port: PORT,
128+
..Default::default()
129+
})
130+
.await;
131+
132+
let client = server.register_client(serde_json::json!({
133+
"scope": "openid",
134+
"redirect_uris": [format!("{SCHEME}://{HOST}:{PORT}")],
135+
"client_name": "mock_oauth2"
136+
}));
137+
138+
// Generate a valid access token and persist it to the credentials,
139+
// otherwise the client won't make a request with an invalid access token.
140+
let token = server.generate_token(&client, JwtOptions::default());
141+
let access_token = token.access_token;
142+
143+
let contents = format!(
144+
r#"
145+
[credentials]
146+
[credential.default]
147+
[credentials.default.token_payload]
148+
access_token = "{access_token}"
149+
"#
150+
);
151+
secrets_toml
152+
.as_file()
153+
.write_all(contents.as_bytes())
154+
.unwrap();
155+
156+
oauth_ready_tx.send(()).unwrap();
157+
158+
server.wait_for_shutdown().await?;
159+
160+
secrets_toml.close().unwrap();
161+
162+
Ok(())
126163
}
127164
}
128165

crates/lib/tests/secrets.toml

Lines changed: 0 additions & 6 deletions
This file was deleted.

crates/lib/tests/settings.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ default_profile_name = "default"
44
[profiles.default]
55
auth_server_name = "default"
66
credentials_name = "default"
7-
api_url = "http://localhost:8000"
8-
grpc_api_url = "http://localhost:8003"
7+
api_url = "http://127.0.0.1:8000"
8+
grpc_api_url = "http://127.0.0.1:8003"
99

1010
[auth_servers]
1111
[auth_servers.default]
1212
client_id = "default_client_id"
13-
issuer = "http://localhost:8001"
13+
issuer = "http://127.0.0.1:8001"

crates/python/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pyo3-tracing-subscriber = { workspace = true, features = ["pyo3", "layer-otel-ot
3333
quil-rs.workspace = true
3434
serde_json.workspace = true
3535
tokio.workspace = true
36+
tokio-util.workspace = true
3637
thiserror.workspace = true
3738
numpy.workspace = true
3839
rigetti-pyo3.workspace = true

0 commit comments

Comments
 (0)