pub struct Client { /* private fields */ }Expand description
A client for the account endpoints.
Every call takes the token it acts with; the client holds none. It is cheap to clone (the connection pool is shared).
use ccwire_oauth::{Client, Secret};
let client = Client::new()?;
let token = Secret::new(std::env::var("ACCESS_TOKEN").unwrap_or_default());
let usage = client.usage(&token).await?;
if let Some(w) = usage.five_hour {
println!("5h: {:?}% (resets {:?})", w.utilization, w.resets_at);
}Implementations§
Source§impl Client
impl Client
Sourcepub fn builder() -> ClientBuilder
pub fn builder() -> ClientBuilder
A ClientBuilder with the defaults.
Sourcepub async fn usage(&self, token: &Secret) -> Result<Usage, Error>
pub async fn usage(&self, token: &Secret) -> Result<Usage, Error>
GET /api/oauth/usage: the 5-hour and 7-day windows and extra
usage.
§Errors
Error; a token without the profile scope is
Error::Forbidden.
Sourcepub async fn profile(&self, token: &Secret) -> Result<Profile, Error>
pub async fn profile(&self, token: &Secret) -> Result<Profile, Error>
GET /api/oauth/profile: the account and its organization.
§Errors
Error; an inference-only token is Error::Forbidden.
Sourcepub async fn validate(&self, token: &Secret) -> Result<(), Error>
pub async fn validate(&self, token: &Secret) -> Result<(), Error>
POST /api/oauth/validate: whether the token is good, without
reading anything. Ok(()) if it is.
The endpoint accepts tokens with user:profile (a subscription
login) and refuses inference-only ones.
§Errors
Error::Unauthorized for a bad, expired or revoked token,
Error::Forbidden for one without an accepted scope; Error
otherwise.
Sourcepub async fn prepaid_credits(
&self,
token: &Secret,
org: &str,
) -> Result<PrepaidCredits, Error>
pub async fn prepaid_credits( &self, token: &Secret, org: &str, ) -> Result<PrepaidCredits, Error>
GET /api/oauth/organizations/{org}/prepaid/credits: the prepaid
credit balance. org is Organization::uuid.
§Errors
Error::Config for an empty, . or .. org; Error.
Sourcepub async fn overage_spend_limit(
&self,
token: &Secret,
org: &str,
) -> Result<OverageSpendLimit, Error>
pub async fn overage_spend_limit( &self, token: &Secret, org: &str, ) -> Result<OverageSpendLimit, Error>
GET /api/oauth/organizations/{org}/overage_spend_limit: the extra
usage limit and what’s spent against it. Read-only: this crate never
changes it.
§Errors
Error::Config for an empty, . or .. org; Error.
Sourcepub async fn refresh(&self, req: &RefreshRequest) -> Result<TokenSet, Error>
pub async fn refresh(&self, req: &RefreshRequest) -> Result<TokenSet, Error>
Posts a refresh-token grant to the token endpoint and returns the new tokens.
A plain call: it stores nothing and takes no lock. The posted
refresh token is spent once the server answers, so the caller stores
the result with a compare-and-swap (see TokenSet).
The response is read leniently: as long as it carries the tokens,
they come back, whatever the other fields hold (a missing or odd
expires_in is None).
§Don’t cancel it
Once the request is sent, the server may already have spent the
posted refresh token. Dropping this future then (a select! that
picks another branch, a tokio::time::timeout shorter than the
client’s own, an aborted task) throws away the new tokens, and the
login is lost: the old refresh token is dead and the new one was
never stored. Await it to the end; the client’s own timeout
(ClientBuilder::timeout) bounds it. If the caller itself may be
cancelled, use Client::refresh_and_store, which runs the refresh
and the store in a task of their own.
§Errors
Error::Unauthorized with code invalid_grant for a revoked or
already-rotated refresh token (a new login is needed);
Error::Decode only for a body without the tokens; Error
otherwise.
Sourcepub fn refresh_and_store<F, Fut, R>(
&self,
req: RefreshRequest,
store: F,
) -> impl Future<Output = Result<R, Error>> + Send + 'static
pub fn refresh_and_store<F, Fut, R>( &self, req: RefreshRequest, store: F, ) -> impl Future<Output = Result<R, Error>> + Send + 'static
Client::refresh, then store with the new tokens, both in a
task of their own (tokio::spawn): once this is called they run to
the end even if the returned future is dropped, so a cancelled
caller can’t lose a rotated login.
store runs only on success, and gets the TokenSet; do the
compare-and-swap there. The returned future gives store’s output.
The task starts when this is called, not when the future is first
polled.
use ccwire_oauth::{RefreshRequest, Secret};
let req = RefreshRequest::new(Secret::new("stored-refresh-token"));
let access = client
.refresh_and_store(req, |tokens| async move {
// Compare-and-swap tokens.refresh_token into the store here.
tokens.access_token
})
.await?;§Errors
As Client::refresh; Error::Cancelled if the task was
cancelled (its runtime shut down).
§Panics
Outside a tokio runtime. A panic in store resumes in the caller.