origincloud_core/lib.rs
1//! What every service crate of the Origin Cloud SDK shares: credentials,
2//! endpoints, retries, errors, long-running operations and
3//! read-your-writes.
4//!
5//! A program creates one [`Client`] and hands it to the clients of the
6//! services it uses:
7//!
8//! ```no_run
9//! # fn main() -> Result<(), origincloud_core::Error> {
10//! let client = origincloud_core::Client::new()?;
11//! // let volumes = origincloud_pvs::v1beta1::VolumeClient::new(&client);
12//! # Ok(())
13//! # }
14//! ```
15//!
16//! # Credentials
17//!
18//! Unless [`ClientBuilder::credentials`] names them, [`Client::new`] takes
19//! the first of (natively; in a browser there are none of these, see
20//! [`credentials`]):
21//!
22//! 1. `ORIGINCLOUD_ACCESS_TOKEN`: an access token, sent as it is.
23//! 2. `ORIGINCLOUD_CLIENT_ID` and `ORIGINCLOUD_CLIENT_SECRET`: a service
24//! account's credential, exchanged for access tokens as they expire.
25//! 3. A CI job's OIDC token, exchanged at the security token service for a
26//! `WORKLOAD` service account's access tokens
27#![cfg_attr(not(target_arch = "wasm32"), doc = " ([`Federated`]):")]
28#![cfg_attr(target_arch = "wasm32", doc = " (`Federated`):")]
29//! from the
30//! file `ORIGINCLOUD_OIDC_TOKEN_FILE` names, from `ORIGINCLOUD_OIDC_TOKEN`,
31//! which holds the token itself, or in a GitHub Actions job granted
32//! `permissions: id-token: write`. `ORIGINCLOUD_OIDC_DISABLED=true`
33//! leaves this step out.
34//! 4. The session the `origin` CLI stored at its last login
35//! (`origin id login`).
36//! 5. On an instance that runs as a service account, its credential from
37//! the instance's metadata service. `ORIGINCLOUD_METADATA_DISABLED=true`
38//! leaves this step out.
39//!
40//! # Endpoints
41//!
42//! Every service answers at `{service}.origincloudapis.eu`, which serves
43//! every resource wherever it lives. [`ClientBuilder::region`], or
44//! `ORIGINCLOUD_REGION`, picks a region's front door instead,
45//! `{service}.{region}.origincloudapis.eu`, for the services that have
46//! regional ones.
47//!
48//! # Retries
49//!
50//! A call the API answers as retryable is sent again, after the delay the
51//! answer asks for or a growing one, as long as sending it again is safe: a
52//! read, or a mutation with a request id. The client fills in a mutation's
53//! request id when it is empty, so that a retry can't apply it twice.
54//! [`ClientBuilder::max_attempts`] bounds the attempts of every call;
55//! [`CallOptions`] those of the calls through one service client, such as
56//! a mutation that must not be sent twice at all.
57//!
58//! # Read-your-writes
59//!
60//! The API answers reads from replicas that can be a moment behind a write
61//! that just succeeded. The client keeps the consistency token each
62//! mutation and each finished operation answers with, and sends it with
63//! every later call, so that a read through the same client shows the
64//! client's own writes. The token answered last replaces the one held: for
65//! writes made one after another, it covers them all; of writes made
66//! concurrently, it covers the one answered last (see
67//! [`Client::set_consistency_token`]). The token is kept per client, and
68//! shared by its clones; clients given one store share it
69//! ([`ClientBuilder::consistency_token_store`]). A call the API refuses
70//! because its token isn't one is sent again once without it, and the
71//! token is dropped.
72//!
73//! # Names
74//!
75//! A resource's `name` is its relative name; its full resource name,
76//! `//{service}.origincloudapis.eu/{name}`, is what references carry
77//! ([`FullName`]). A request's path fields take either. A full name is
78//! checked against the type the field refers to (the client's own service
79//! for its own resources, resourcemanager for a project or an organization
80//! as a `parent`) and sent as its relative name; one on any other host is
81//! refused before anything is sent.
82//!
83//! # Errors
84//!
85//! A call the API refuses returns an [`Error::Api`]: branch on
86//! [`Error::reason`], the platform's error code. [`ApiError::metadata`]
87//! says more about some reasons, such as the name of the resource a
88//! `CONFLICT` is about.
89//!
90//! # Runtime
91//!
92//! The clients are `async` and send their calls with
93//! [reqwest](https://docs.rs/reqwest), on [Tokio](https://tokio.rs).
94//!
95//! They also build for the browser, `wasm32-unknown-unknown`, where
96//! reqwest sends calls with the browser's `fetch`, retries wait on
97//! `setTimeout`, and no Tokio runtime is needed: run the futures with the
98//! UI framework's executor, such as `wasm-bindgen-futures`. There, the
99//! futures the SDK returns aren't `Send` ([`MaybeSend`]), the credentials
100//! are the page's own (see [`credentials`]), and the per-call
101//! [`ClientBuilder::timeout`] is the only one, as a browser's `fetch` has
102//! no connect timeout.
103
104#![warn(missing_docs)]
105
106mod client;
107pub mod consistency;
108pub mod credentials;
109mod error;
110pub mod longrunning;
111pub mod names;
112mod operation;
113mod pages;
114pub mod protobuf;
115pub mod rpc;
116mod runtime;
117mod transport;
118pub mod types;
119
120pub use client::{CallOptions, Client, ClientBuilder, Service, VERSION};
121pub use consistency::{ConsistencyTokenCell, ConsistencyTokenStore};
122#[cfg(any(not(target_arch = "wasm32"), feature = "service-account-wasm"))]
123pub use credentials::ServiceAccount;
124pub use credentials::{AccessToken, Credentials};
125#[cfg(not(target_arch = "wasm32"))]
126pub use credentials::{CliSession, Federated, InstanceMetadata, default_credentials};
127pub use error::{ApiError, Error, FieldViolation};
128pub use names::{FullName, FullNameError};
129pub use operation::Operation;
130pub use pages::Pages;
131pub use runtime::{MaybeSend, MaybeSendFuture};
132pub use transport::{
133 CONSISTENCY_TOKEN_HEADER, HttpBody, Method, Query, Request, json_body, new_request_id,
134 path_value, query,
135};