Skip to content
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

Client split #525

Merged
merged 5 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ body:
id: language-version
attributes:
label: Rust version
placeholder: Our MSRV is 1.73.0
placeholder: Our MSRV is 1.75.0
validations:
required: true
- type: input
Expand Down
76 changes: 44 additions & 32 deletions .github/workflows/async-stripe.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:

env:
RUSTFLAGS: -Dwarnings
rust_min: 1.73
rust_min: 1.75

jobs:
format:
Expand Down Expand Up @@ -74,16 +74,14 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
runtime:
features:
[
default-tls,
rustls-tls-webpki-roots,
rustls-tls-native,
async-std-surf,
tokio-hyper,
tokio-hyper-rustls,
tokio-hyper-rustls-webpki,
blocking,
blocking-rustls,
blocking-rustls-webpki,
]

steps:
- name: Checkout sources
uses: actions/checkout@v3
Expand All @@ -97,21 +95,46 @@ jobs:
- uses: Swatinem/rust-cache@v2

- name: Run clippy
run: cargo clippy --features "runtime-${{ matrix.runtime }} full serialize deserialize"
run: cargo clippy --no-default-features --features "full serialize deserialize ${{ matrix.features }}"

test:
test-clients:
name: Test Clients
runs-on: ubuntu-latest
strategy:
matrix:
runtime:
features:
[
default-tls,
rustls-tls-webpki-roots,
rustls-tls-native,
async-std-surf,
tokio-hyper,
tokio-hyper-rustls,
tokio-hyper-rustls-webpki,
blocking,
blocking-rustls,
blocking-rustls-webpki,
]

steps:
- name: Checkout sources
uses: actions/checkout@v3

- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.rust_min }}
components: clippy

- uses: Swatinem/rust-cache@v2

- name: Run tests
run: cargo test --no-default-features --features "full serialize deserialize ${{ matrix.features }}"

stripe-mock-tests:
name: Stripe Mock Tests
runs-on: ubuntu-latest
strategy:
matrix:
features:
[
default-tls,
rustls-tls-webpki-roots,
rustls-tls-native,
]
services:
stripe-mock:
Expand All @@ -128,20 +151,9 @@ jobs:
toolchain: ${{ env.rust_min }}

- uses: Swatinem/rust-cache@v2
- name: Run stripe mock tests
run: cargo test -p async-stripe-tests --no-default-features --features ${{ matrix.features }}

- uses: taiki-e/install-action@cargo-llvm-cov
- name: Test and gather coverage
run: cargo llvm-cov --lcov --output-path lcov.info --features "runtime-${{ matrix.runtime }} serialize deserialize"
- name: Upload to codecov.io
uses: codecov/codecov-action@v2.1.0
with:
token: ${{secrets.CODECOV_TOKEN}}
files: lcov.info
- name: Archive code coverage results
uses: actions/upload-artifact@v1
with:
name: code-coverage-report
path: lcov.info

docs:
name: Docs
Expand All @@ -155,7 +167,7 @@ jobs:
- uses: Swatinem/rust-cache@v2

- name: Build Documentation
run: cargo doc --lib --no-deps --features "runtime-tokio-hyper full"
run: cargo doc --lib --no-deps --features "full serialize deserialize"

# Examples tested separately so that we can use crates which don't match our MSRV
examples:
Expand All @@ -170,4 +182,4 @@ jobs:
- uses: Swatinem/rust-cache@v2

- name: Check examples
run: cargo clippy --features "runtime-tokio-hyper-rustls" --workspace
run: cargo clippy --workspace
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ to run `cargo fmt` to make sure it conforms to the standard.
This library is (mostly) authored via code generation by parsing the OpenAPI specification for Stripe.
It consists of 3 main pieces:
- `async-stripe`: The definition of the `Stripe` client
- `stripe_types`: Core type definitions, used a ton in generated code
- `async-stripe-types`: Core type definitions, used a ton in generated code
- `generated/*`: Generated crates which implement `Stripe` API requests and related types.
- `stripe_webhook`: Glue code for parsing and validating `Stripe` webhook events and generated
- `async-stripe-webhook`: Glue code for parsing and validating `Stripe` webhook events and generated
code for deserializing the events themselves.

No changes should be made to code in a `generated/*` folder. If you'd like to change that
Expand Down
17 changes: 9 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
[workspace]
members = [
"async-stripe",
"stripe_types",
"stripe_webhook",
"async-stripe-types",
"async-stripe-webhook",
"tests",
"generated/*",
"examples/*",
"async-stripe-client-core",
"async-stripe",
]

# Skip "examples/*" when running default commands since that adds a bunch of deps that makes
# development feedback loop slower
default-members = ["async-stripe", "stripe_types", "stripe_webhook", "tests", "generated/*"]
# development feedback loop slower.
default-members = ["async-stripe", "async-stripe-types", "async-stripe-webhook", "async-stripe-client-core", "generated/*"]
resolver = "2"
# Makes dependency management simpler to allow codegen crate to use whichever dep versions
# it wants without affecting dependency resolution of the user-facing library crates
Expand All @@ -19,7 +20,7 @@ exclude = ["openapi"]
[workspace.package]
version = "0.22.2"
description = "API bindings for the Stripe HTTP API"
rust-version = "1.73.0"
rust-version = "1.75.0"
authors = [
"Anna Baldwin <abaldwin@developers.wyyerd.com>",
"Kevin Stenerson <kestred@users.noreply.github.com>",
Expand All @@ -37,7 +38,7 @@ edition = "2021"

[workspace.dependencies]
serde = { version = ">=1.0.79", features = ["derive"] } # we use `serde(other)` which was introduced in 1.0.79
serde_json = "1"
http-types = { version = "2.12.0", default-features = false }
smol_str = { version = "0.2.0", features = ["serde"] }
miniserde = "0.1.34"
serde_json = "1.0.115"
serde_qs = "0.12.0"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ If you don't see the specific version you are on, prefer the next available vers

## MSRV

We currently have `1.73.0` pinned in CI, so any version of rustc newer than that should
We currently have `1.75.0` pinned in CI, so any version of rustc newer than that should
work.
If this is not the case, please open an issue. As a policy, we permit MSRV increases in
non-breaking releases.
Expand Down
28 changes: 28 additions & 0 deletions async-stripe-client-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "async-stripe-client-core"
version.workspace = true
description.workspace = true
rust-version.workspace = true
authors.workspace = true
license.workspace = true
readme.workspace = true
homepage.workspace = true
repository.workspace = true
documentation.workspace = true
keywords.workspace = true
categories.workspace = true
edition.workspace = true

[lib]
name = "stripe_client_core"

[dependencies]
async-stripe-shared = { path = "../generated/async-stripe-shared" }
async-stripe-types = { path = "../async-stripe-types" }
serde_json.workspace = true
serde.workspace = true
serde_qs.workspace = true
bytes = "1.6.0"
miniserde.workspace = true
futures-util = "0.3.28"
uuid = { version = "1.6.1", optional = true, features = ["v4"] }
139 changes: 139 additions & 0 deletions async-stripe-client-core/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
use std::fmt;
use std::fmt::{Display, Formatter};

use stripe_shared::version::VERSION;
use stripe_shared::{AccountId, ApiVersion, ApplicationId};

use crate::RequestStrategy;

/// Shared configuration utilities for implementing a Stripe client.
///
/// This is meant for internal use when implementing compatible clients,
/// so it may be more unstable with respect to semver.

// The disclaimer above was written to justify the semver hazard of keeping all the fields here public.
// This is not necessary, but writing accessors is tricky because configs using this
// internally want to take ownership of each field to avoid unnecessary clones.
#[derive(Clone)]
pub struct SharedConfigBuilder {
/// The stripe version being used.
pub stripe_version: ApiVersion,
/// The user-provided part of the user-agent we'll use.
pub app_info_str: Option<String>,
/// The client id to send requests with.
pub client_id: Option<ApplicationId>,
/// The account id to send requests with.
pub account_id: Option<AccountId>,
/// The default request strategy to use.,
pub request_strategy: Option<RequestStrategy>,
/// The secret key for authorizing requests.
pub secret: String,
/// The base URL to send requests to.
pub api_base: Option<String>,
}

impl SharedConfigBuilder {
/// Create a new `SharedConfigBuilder` with the given secret key.
pub fn new(secret: impl Into<String>) -> Self {
Self {
stripe_version: VERSION,
app_info_str: None,
client_id: None,
account_id: None,
request_strategy: None,
secret: secret.into(),
api_base: None,
}
}

/// Set the Stripe client id for the client. This will be sent to stripe
/// with the "client-id" header.
pub fn client_id(mut self, client_id: ApplicationId) -> Self {
self.client_id = Some(client_id);
self
}

/// Set the default Stripe account id used for the client. This will be sent to stripe
/// with the "stripe-account" header, unless it is overridden when customizing
/// a request.
pub fn account_id(mut self, account_id: AccountId) -> Self {
self.account_id = Some(account_id);
self
}

/// Sets the default `RequestStrategy` used when making requests.
pub fn request_strategy(mut self, strategy: RequestStrategy) -> Self {
self.request_strategy = Some(strategy);
self
}

/// Create a new client pointed at a specific URL. This is useful for testing.
pub fn url(mut self, url: impl Into<String>) -> Self {
self.api_base = Some(url.into());
self
}

/// Set the application info for the client.
pub fn app_info(
mut self,
name: impl Into<String>,
version: Option<String>,
url: Option<String>,
) -> Self {
self.app_info_str = Some(AppInfo { name: name.into(), url, version }.to_string());
self
}
}

struct AppInfo {
name: String,
url: Option<String>,
version: Option<String>,
}

impl Display for AppInfo {
/// Formats a plugin's 'App Info' into a string that can be added to the end of a User-Agent string.
///
/// This formatting matches that of other libraries, and if changed then it should be changed everywhere.
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let name = &self.name;
match (&self.version, &self.url) {
(Some(a), Some(b)) => write!(f, "{name}/{a} ({b})"),
(Some(a), None) => write!(f, "{name}/{a}"),
(None, Some(b)) => write!(f, "{name} ({b})"),
_ => f.write_str(name),
}
}
}

// Manual implementation so we don't print the secret!
impl fmt::Debug for SharedConfigBuilder {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let mut builder = f.debug_struct("SharedConfigBuilder");
builder.field("request_strategy", &self.request_strategy);
builder.field("client_id", &self.client_id);
builder.field("account_id", &self.account_id);
builder.field("app_info_str", &self.app_info_str);
if let Some(api_base) = &self.api_base {
builder.field("api_base", api_base);
}
builder.field("stripe_version", &self.stripe_version);
builder.finish()
}
}

/// Per-request configuration overrides.
#[derive(Debug)]
#[non_exhaustive]
pub struct ConfigOverride {
/// Use a particular account id, instead of the client default.
pub account_id: Option<AccountId>,
/// Use a particular `RequestStrategy`, instead of the client default.
pub request_strategy: Option<RequestStrategy>,
}

impl ConfigOverride {
pub(crate) fn new() -> Self {
Self { account_id: None, request_strategy: None }
}
}
17 changes: 17 additions & 0 deletions async-stripe-client-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//! This crate provides shared utilities for implementing clients
//! capable of making Stripe API requests.

#![warn(clippy::missing_errors_doc, clippy::missing_panics_doc)]
#![deny(missing_docs, missing_debug_implementations)]
#![forbid(unsafe_code)]
mod config;
mod pagination;
mod request_strategy;
mod stripe_request;

pub use config::{ConfigOverride, SharedConfigBuilder};
pub use pagination::*;
pub use request_strategy::*;
pub use stripe_request::*;
pub use stripe_shared::version::VERSION;
pub use stripe_shared::{AccountId, ApiVersion, ApplicationId};
Loading
Loading