Skip to content
Closed
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.140"
thiserror = "2.0.0"
url = { version = "2.5.4", features = ["serde"] }
urlencoding = "2.1.3"

[dev-dependencies]
matches = "0.1.10"
Expand Down
2 changes: 1 addition & 1 deletion src/admin_portal/operations/generate_portal_link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ impl GeneratePortalLink for AdminPortal<'_> {
.json(&params)
.send()
.await?
.handle_unauthorized_or_generic_error()?
.handle_unauthorized_or_generic_error().await?
.json::<GeneratePortalLinkResponse>()
.await?;

Expand Down
9 changes: 9 additions & 0 deletions src/core/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ pub enum WorkOsError<E> {
/// An unhandled error occurred with the API request.
#[error("request error")]
RequestError(#[from] reqwest::Error),

/// An API error with status code and response body.
#[error("API error {status}: {body}")]
ApiError {
/// The HTTP status code returned by the API.
status: reqwest::StatusCode,
/// The response body text containing error details.
body: String,
},
}

/// A WorkOS SDK result.
Expand Down
23 changes: 13 additions & 10 deletions src/core/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,36 @@ where
{
/// Handles an unauthorized error from the WorkOS API by converting it into a
/// [`WorkOsError::Unauthorized`] response.
fn handle_unauthorized_error<E>(self) -> WorkOsResult<Self, E>;
async fn handle_unauthorized_error<E>(self) -> WorkOsResult<Self, E>;

/// Handles a generic error from the WorkOS API by converting it into a
/// [`WorkOsError::RequestError`] response.
fn handle_generic_error<E>(self) -> WorkOsResult<Self, E>;
async fn handle_generic_error<E>(self) -> WorkOsResult<Self, E>;

/// Handles an unauthorized or generic error from the WorkOS API.
fn handle_unauthorized_or_generic_error<E>(self) -> WorkOsResult<Self, E>;
async fn handle_unauthorized_or_generic_error<E>(self) -> WorkOsResult<Self, E>;
}

impl ResponseExt for Response {
fn handle_unauthorized_error<E>(self) -> WorkOsResult<Self, E> {
async fn handle_unauthorized_error<E>(self) -> WorkOsResult<Self, E> {
if self.status() == StatusCode::UNAUTHORIZED {
Err(WorkOsError::Unauthorized)
} else {
Ok(self)
}
}

fn handle_generic_error<E>(self) -> WorkOsResult<Self, E> {
match self.error_for_status() {
Ok(response) => Ok(response),
Err(err) => Err(WorkOsError::RequestError(err)),
async fn handle_generic_error<E>(self) -> WorkOsResult<Self, E> {
if self.status().is_success() {
Ok(self)
} else {
let status = self.status();
let body = self.text().await.unwrap_or_else(|_| "Failed to read response body".to_string());
Err(WorkOsError::ApiError { status, body })
}
}

fn handle_unauthorized_or_generic_error<E>(self) -> WorkOsResult<Self, E> {
self.handle_unauthorized_error()?.handle_generic_error()
async fn handle_unauthorized_or_generic_error<E>(self) -> WorkOsResult<Self, E> {
self.handle_unauthorized_error().await?.handle_generic_error().await
}
}
2 changes: 1 addition & 1 deletion src/directory_sync/operations/delete_directory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ impl DeleteDirectory for DirectorySync<'_> {
.bearer_auth(self.workos.key())
.send()
.await?
.handle_unauthorized_or_generic_error()?;
.handle_unauthorized_or_generic_error().await?;

Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion src/directory_sync/operations/get_directory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ impl GetDirectory for DirectorySync<'_> {
.bearer_auth(self.workos.key())
.send()
.await?
.handle_unauthorized_or_generic_error()?
.handle_unauthorized_or_generic_error().await?
.json::<Directory>()
.await?;

Expand Down
2 changes: 1 addition & 1 deletion src/directory_sync/operations/get_directory_group.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ impl GetDirectoryGroup for DirectorySync<'_> {
.bearer_auth(self.workos.key())
.send()
.await?
.handle_unauthorized_or_generic_error()?
.handle_unauthorized_or_generic_error().await?
.json::<DirectoryGroup>()
.await?;

Expand Down
2 changes: 1 addition & 1 deletion src/directory_sync/operations/get_directory_user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ impl GetDirectoryUser for DirectorySync<'_> {
.bearer_auth(self.workos.key())
.send()
.await?
.handle_unauthorized_or_generic_error()?
.handle_unauthorized_or_generic_error().await?
.json::<DirectoryUser>()
.await?;

Expand Down
2 changes: 1 addition & 1 deletion src/directory_sync/operations/list_directories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl ListDirectories for DirectorySync<'_> {
.bearer_auth(self.workos.key())
.send()
.await?
.handle_unauthorized_or_generic_error()?
.handle_unauthorized_or_generic_error().await?
.json::<PaginatedList<Directory>>()
.await?;

Expand Down
2 changes: 1 addition & 1 deletion src/directory_sync/operations/list_directory_groups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ impl ListDirectoryGroups for DirectorySync<'_> {
.bearer_auth(self.workos.key())
.send()
.await?
.handle_unauthorized_or_generic_error()?
.handle_unauthorized_or_generic_error().await?
.json::<PaginatedList<DirectoryGroup>>()
.await?;

Expand Down
2 changes: 1 addition & 1 deletion src/directory_sync/operations/list_directory_users.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ impl ListDirectoryUsers for DirectorySync<'_> {
.bearer_auth(self.workos.key())
.send()
.await?
.handle_unauthorized_or_generic_error()?
.handle_unauthorized_or_generic_error().await?
.json::<PaginatedList<DirectoryUser>>()
.await?;

Expand Down
2 changes: 1 addition & 1 deletion src/mfa/operations/challenge_factor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ impl ChallengeFactor for Mfa<'_> {
.json(&params)
.send()
.await?
.handle_unauthorized_or_generic_error()?
.handle_unauthorized_or_generic_error().await?
.json::<AuthenticationChallenge>()
.await?;

Expand Down
2 changes: 1 addition & 1 deletion src/mfa/operations/enroll_factor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ impl EnrollFactor for Mfa<'_> {
.json(&params)
.send()
.await?
.handle_unauthorized_error()?
.handle_unauthorized_error().await?
.handle_enroll_factor_error()
.await?
.json::<AuthenticationFactor>()
Expand Down
2 changes: 1 addition & 1 deletion src/mfa/operations/verify_challenge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ impl VerifyChallenge for Mfa<'_> {
.json(&params)
.send()
.await?
.handle_unauthorized_or_generic_error()?
.handle_unauthorized_or_generic_error().await?
.json::<VerifyChallengeResponse>()
.await?;

Expand Down
2 changes: 2 additions & 0 deletions src/organizations/operations.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
mod create_organization;
mod delete_organization;
mod get_organization;
mod get_organization_by_external_id;
mod list_organizations;
mod update_organization;

pub use create_organization::*;
pub use delete_organization::*;
pub use get_organization::*;
pub use get_organization_by_external_id::*;
pub use list_organizations::*;
pub use update_organization::*;
43 changes: 24 additions & 19 deletions src/organizations/operations/create_organization.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
use std::collections::HashSet;

use async_trait::async_trait;
use serde::Serialize;
use thiserror::Error;

use crate::organizations::{Organization, Organizations};
use crate::{ResponseExt, WorkOsError, WorkOsResult};
use crate::{Metadata, ResponseExt, WorkOsError, WorkOsResult};

#[derive(Debug, Serialize)]
/// The data for an organization domain.
pub struct OrganizationDomainData<'a> {
/// The domain to be added to the organization. This should be a domain owned by the organization, and not a common consumer domain like gmail.com.
pub domain: &'a str,
/// The verification state of the domain. 'pending' or 'verified'
pub state: &'a str,
}

/// The parameters for [`CreateOrganization`].
#[derive(Debug, Serialize)]
pub struct CreateOrganizationParams<'a> {
/// The name of the organization.
/// A descriptive name for the Organization. This field does not need to be unique.
pub name: &'a str,

/// Whether the connections within this organization should allow profiles
/// that do not have a domain that is present in the set of the organization's
/// user email domains.
///
/// See [here](https://workos.com/docs/sso/guide/frequently-asked-questions#allow-profiles-outside-organization)
/// for more details.
pub allow_profiles_outside_organization: Option<&'a bool>,

/// The domains of the organization.
///
/// At least one domain is required unless `allow_profiles_outside_organization` is `true`.
pub domains: HashSet<&'a str>,
pub domain_data: Vec<OrganizationDomainData<'a>>,
/// The external ID of the Organization.
pub external_id: Option<&'a str>,
/// Object containing metadata key/value pairs associated with the organization.
pub metadata: Option<Metadata>,
}

/// An error returned from [`CreateOrganization`].
Expand Down Expand Up @@ -88,7 +88,8 @@ impl CreateOrganization for Organizations<'_> {
.json(&params)
.send()
.await?
.handle_unauthorized_or_generic_error()?
.handle_unauthorized_or_generic_error()
.await?
.json::<Organization>()
.await?;

Expand Down Expand Up @@ -144,8 +145,12 @@ mod test {
.organizations()
.create_organization(&CreateOrganizationParams {
name: "Foo Corp",
allow_profiles_outside_organization: Some(&false),
domains: HashSet::from(["foo-corp.com"]),
domain_data: vec![OrganizationDomainData {
domain: "foo-corp.com",
state: "pending",
}],
external_id: None,
metadata: None,
})
.await
.unwrap();
Expand Down
2 changes: 1 addition & 1 deletion src/organizations/operations/delete_organization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ impl DeleteOrganization for Organizations<'_> {
.bearer_auth(self.workos.key())
.send()
.await?
.handle_unauthorized_or_generic_error()?;
.handle_unauthorized_or_generic_error().await?;

Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion src/organizations/operations/get_organization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ impl GetOrganization for Organizations<'_> {
.bearer_auth(self.workos.key())
.send()
.await?
.handle_unauthorized_or_generic_error()?
.handle_unauthorized_or_generic_error().await?
.json::<Organization>()
.await?;

Expand Down
Loading
Loading