Skip to content
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ SPDX-License-Identifier: Apache-2.0

All notable changes to this project will be documented in this file.

## [0.12.0] - 2026-04-23

### Features

- [**breaking**] Add org_id to list_user_metadata
- [**breaking**] Add org_id to list_users
- [**breaking**] Add org_id to user metadata functions

### Refactor

- [**breaking**] Change org_id param order

## [0.11.1] - 2026-04-20

### Bug Fixes
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[package]
name = "famedly-zitadel-rust-client"
description = "HTTP client for Zitadel IdP built by Famedly"
version = "0.11.1"
version = "0.12.0"
authors = []
edition = "2024"
resolver = "2"
Expand Down
23 changes: 19 additions & 4 deletions src/v2/users/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,14 @@ impl Zitadel {
pub fn list_idp_links(
&self,
user_id: &str,
org_id: Option<String>,
params: Option<PaginationParams>,
) -> Result<impl Stream<Item = Result<IdpLink>> + Send> {
Ok(PaginationHandler::new(
self.clone(),
params,
self.make_url(&format!("v2/users/{user_id}/links/_search"))?,
None, // Endpoint does not support org_id
org_id,
))
}
/// List passkeys of an user
Expand All @@ -167,6 +168,7 @@ impl Zitadel {
/// [Docs](https://zitadel.com/docs/apis/resources/user_service_v2/user-service-list-users)
pub fn list_users(
&self,
org_id: Option<String>,
params: Option<PaginationParams>,
sorting: Option<UserFieldName>,
queries: Option<Vec<SearchQuery>>,
Expand All @@ -175,7 +177,7 @@ impl Zitadel {
self.clone(),
(params, sorting, queries),
self.make_url("v2/users")?,
None,
org_id,
))
}

Expand Down Expand Up @@ -574,10 +576,16 @@ impl Zitadel {
Ok(self.send_request(request).await?)
}
/// Remove a metadata object from a user with a specific key
pub async fn delete_user_metadata(&self, user_id: &str, key: &str) -> Result<Details> {
pub async fn delete_user_metadata(
&self,
user_id: &str,
key: &str,
org_id: Option<String>,
) -> Result<Details> {
let request = self
.client
.delete(self.make_url(&format!("/management/v1/users/{user_id}/metadata/{key}"))?)
.chain_opt(org_id, |req, org_id| req.header(HEADER_ZITADEL_ORGANIZATION_ID, org_id))
.build()?;

Ok(self.send_request(request).await?)
Expand All @@ -589,10 +597,12 @@ impl Zitadel {
user_id: &str,
key: &str,
value: &str,
org_id: Option<String>,
) -> Result<SetUserMetadataResponse> {
let request = self
.client
.post(self.make_url(&format!("/management/v1/users/{user_id}/metadata/{key}"))?)
.chain_opt(org_id, |req, org_id| req.header(HEADER_ZITADEL_ORGANIZATION_ID, org_id))
.json(&serde_json::json!({"value": BASE64_STANDARD.encode(value)}))
.build()?;

Expand All @@ -604,10 +614,12 @@ impl Zitadel {
&self,
user_id: &str,
body: Vec<SetMetadataEntry>,
org_id: Option<String>,
) -> Result<Details> {
let request = self
.client
.post(self.make_url(&format!("/management/v1/users/{user_id}/metadata/_bulk"))?)
.chain_opt(org_id, |req, org_id| req.header(HEADER_ZITADEL_ORGANIZATION_ID, org_id))
.json(&serde_json::json!({"metadata": body}))
.build()?;

Expand All @@ -618,10 +630,12 @@ impl Zitadel {
&self,
user_id: &str,
body: Vec<String>,
org_id: Option<String>,
) -> Result<Details> {
let request = self
.client
.delete(self.make_url(&format!("/management/v1/users/{user_id}/metadata/_bulk"))?)
.chain_opt(org_id, |req, org_id| req.header(HEADER_ZITADEL_ORGANIZATION_ID, org_id))
.json(&serde_json::json!({"keys": body}))
.build()?;

Expand All @@ -632,14 +646,15 @@ impl Zitadel {
pub fn list_user_metadata(
&self,
user_id: &str,
org_id: Option<String>,
params: Option<PaginationParams>,
queries: Option<Vec<KeyQuery>>,
) -> Result<impl Stream<Item = Result<UserMetadataResponse>> + Send> {
Ok(PaginationHandler::new(
self.clone(),
(params, queries),
self.make_url(&format!("/management/v1/users/{user_id}/metadata/_search"))?,
None, // TODO: Breaking change -> possibility to add org_id to function args
org_id,
))
}
}
24 changes: 14 additions & 10 deletions tests/e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ async fn tear_down(zitadel: &Zitadel) {
let queries = Some(vec![SearchQuery::new().with_type_query(TypeQuery::new(Userv2Type::Human))]);

zitadel
.list_users(None, None, queries)
.list_users(None, None, None, queries)
.expect("Error getting the list of users for tear down")
.try_for_each(|user| async move {
tracing::info!("user: {user:?}");
Expand Down Expand Up @@ -494,12 +494,13 @@ async fn test_e2e_create_organization() -> Result<()> {
assert_eq!(
zitadel
.list_users(
None,
None,
None,
Some(vec![
SearchQuery::new()
.with_last_name_query(LastNameQuery::new("Adminmann".to_owned()))
])
]),
)?
.count()
.await,
Expand Down Expand Up @@ -816,7 +817,7 @@ async fn test_e2e_list_human_users() -> Result<()> {
Some(vec![SearchQuery::new().with_last_name_query(LastNameQuery::new("Doe".to_owned()))]);
let pagination_params = Some(PaginationParams::DEFAULT.with_asc(true).with_page_size(5));

let stream = zitadel.list_users(pagination_params, None, queries)?;
let stream = zitadel.list_users(None, pagination_params, None, queries)?;

let res_user_emails: Vec<_> = stream
.and_then(|user| {
Expand Down Expand Up @@ -854,7 +855,7 @@ async fn test_e2e_list_idp_links() -> Result<()> {
let id = ret.user_id().expect("Couldn't get the user id from response");
let pagination_params = Some(PaginationParams::DEFAULT.with_asc(true));

let stream = zitadel.list_idp_links(id, pagination_params)?;
let stream = zitadel.list_idp_links(id, None, pagination_params)?;

assert_eq!(stream.count().await, idp_links.len());

Expand Down Expand Up @@ -962,14 +963,14 @@ async fn test_e2e_delete_user() -> Result<()> {
let pagination_params = Some(PaginationParams::DEFAULT.with_asc(true));

assert_eq!(
zitadel.list_users(pagination_params.clone(), None, queries.clone())?.count().await,
zitadel.list_users(None, pagination_params.clone(), None, queries.clone())?.count().await,
2
);

zitadel.delete_user(&id).await?;

let user_count = zitadel
.list_users(pagination_params, None, queries)?
.list_users(None, pagination_params, None, queries)?
.inspect_ok(|user| {
let user_name = user
.human()
Expand Down Expand Up @@ -1010,7 +1011,7 @@ async fn test_e2e_user_metadata() -> Result<()> {
.cloned()
.expect("Couldn't get the user id from response");

zitadel.set_user_metadata(&id, metadata[1].0, metadata[1].1).await?;
zitadel.set_user_metadata(&id, metadata[1].0, metadata[1].1, None).await?;

for (query_key, expected_value) in &metadata {
let ret = zitadel.get_user_metadata(&id, query_key, None).await?;
Expand All @@ -1022,11 +1023,11 @@ async fn test_e2e_user_metadata() -> Result<()> {
assert_eq!(*expected_value, value);
}

zitadel.delete_user_metadata(&id, metadata[1].0).await?;
zitadel.delete_user_metadata(&id, metadata[1].0, None).await?;
assert!(zitadel.get_user_metadata(&id, metadata[1].0, None).await.is_err());

let updated_value = "new_value";
zitadel.set_user_metadata(&id, metadata[0].0, updated_value).await?;
zitadel.set_user_metadata(&id, metadata[0].0, updated_value, None).await?;

let ret = zitadel.get_user_metadata(&id, metadata[0].0, None).await?;
let value = ret
Expand Down Expand Up @@ -1065,11 +1066,12 @@ async fn test_e2e_user_metadata_bulk() -> Result<()> {
.cloned()
.expect("Couldn't get the user id from response");

zitadel.set_user_metadata_bulk(&id, metadata.clone()).await?;
zitadel.set_user_metadata_bulk(&id, metadata.clone(), None).await?;

let stream = zitadel.list_user_metadata(
&id,
None,
None,
Some(vec![KeyQuery::new("key".to_owned()).with_method(TextQueryMethod::Contains)]),
)?;

Expand Down Expand Up @@ -1098,6 +1100,7 @@ async fn test_e2e_user_metadata_bulk() -> Result<()> {
.delete_user_metadata_bulk(
&id,
metadata.iter().map(|metadata| metadata.key().clone()).collect(),
None,
)
.await?;

Expand All @@ -1106,6 +1109,7 @@ async fn test_e2e_user_metadata_bulk() -> Result<()> {
.list_user_metadata(
&id,
None,
None,
Some(vec![KeyQuery::new("key".to_owned()).with_method(TextQueryMethod::Contains)]),
)?
.count()
Expand Down
Loading