Skip to content

Commit

Permalink
feat: Upgrade apis to v2 & allow to select apis via feature flag for …
Browse files Browse the repository at this point in the history
…optimized compile times (#565)

This is achieved by the following steps:

 * generate clients including feature flag
 * guard individual apis with features flags
 * switch from v2beta to v2 apis
  • Loading branch information
sprudel authored Aug 26, 2024
1 parent 1fc3513 commit d1528a2
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 27 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
Cargo.lock
temp*
src/api/generated
.idea
102 changes: 92 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,38 @@ default = []
## Feature that enables support for the [actix framework](https://actix.rs/).
actix = ["credentials", "oidc", "dep:actix-web"]

## The API feature enables the gRPC service clients to access the ZITADEL API.
api = ["dep:prost", "dep:prost-types", "dep:tonic", "dep:tonic-types", "dep:pbjson-types"]
## The API feature enables all gRPC service clients to access the ZITADEL API.
api = [
"api-admin-v1",
"api-auth-v1",
"api-management-v1",
"api-system-v1",
"api-oidc-v2",
"api-org-v2",
"api-session-v2",
"api-settings-v2",
"api-user-v2"
]
## The API feature enables all gRPC service clients to access the respective ZITADEL API.
api-admin-v1 = ["api-common", "zitadel-admin-v1" ]
## The API feature enables all gRPC service clients to access the respective ZITADEL API.
api-auth-v1 = ["api-common", "zitadel-auth-v1" ]
## The API feature enables all gRPC service clients to access the respective ZITADEL API.
api-management-v1 = ["api-common", "zitadel-v1-v1" ]
## The API feature enables all gRPC service clients to access the respective ZITADEL API.
api-system-v1 = ["api-common", "zitadel-system-v1", "zitadel-authn-v1" ]
## The API feature enables all gRPC service clients to access the respective ZITADEL API.
api-oidc-v2 = ["api-common", "zitadel-oidc-v2" ]
## The API feature enables all gRPC service clients to access the respective ZITADEL API.
api-org-v2 = ["api-common", "zitadel-org-v2", "zitadel-user-v2" ]
## The API feature enables all gRPC service clients to access the respective ZITADEL API.
api-session-v2 = ["api-common", "zitadel-session-v2" ]
## The API feature enables all gRPC service clients to access the respective ZITADEL API.
api-settings-v2 = ["api-common", "zitadel-settings-v2" ]
## The API feature enables all gRPC service clients to access the respective ZITADEL API.
api-user-v2 = ["api-common", "zitadel-user-v2" ]
api-common = ["dep:prost", "dep:prost-types", "dep:tonic", "dep:tonic-types", "dep:pbjson-types" ]


## Feature that enables support for the [axum framework](https://docs.rs/axum/latest/axum/).
axum = ["credentials", "oidc", "dep:axum", "dep:axum-extra"]
Expand All @@ -36,7 +66,7 @@ credentials = ["dep:jsonwebtoken", "dep:openidconnect", "dep:reqwest", "dep:serd
## new convenience functions to create a gRPC client with interceptors.
## The interceptors provide easy access to an authenticated ZITADEL API client.
## The interceptors work with the credentials from this crate.
interceptors = ["api", "credentials", "dep:time", "dep:tokio"]
interceptors = ["credentials", "dep:time", "dep:tokio"]

## This feature enables caching of the OIDC discovery and introspection results.
## By default, only the in-memory cache is available. To use a different cache,
Expand All @@ -54,6 +84,59 @@ oidc = ["credentials", "dep:base64-compat"]
## Refer to the rocket module for more information.
rocket = ["credentials", "oidc", "dep:rocket"]

# @@protoc_deletion_point(features)
# This section is automatically generated by protoc-gen-prost-crate.
# Changes in this area may be lost on regeneration.
proto_full = ["zitadel-action-v1","zitadel-admin-v1","zitadel-app-v1","zitadel-auth-v1","zitadel-authn-v1","zitadel-change-v1","zitadel-event-v1","zitadel-feature-v1","zitadel-feature-v2","zitadel-feature-v2beta","zitadel-idp-v1","zitadel-idp-v2","zitadel-instance-v1","zitadel-management-v1","zitadel-member-v1","zitadel-metadata-v1","zitadel-milestone-v1","zitadel-object-v2","zitadel-object-v2beta","zitadel-object-v3alpha","zitadel-oidc-v2","zitadel-oidc-v2beta","zitadel-org-v1","zitadel-org-v2","zitadel-org-v2beta","zitadel-policy-v1","zitadel-project-v1","zitadel-protoc_gen_zitadel-v2","zitadel-quota-v1","zitadel-resources-action-v3alpha","zitadel-resources-object-v3alpha","zitadel-resources-webkey-v3alpha","zitadel-session-v2","zitadel-session-v2beta","zitadel-settings-object-v3alpha","zitadel-settings-v1","zitadel-settings-v2","zitadel-settings-v2beta","zitadel-system-v1","zitadel-text-v1","zitadel-user-schema-v3alpha","zitadel-user-v1","zitadel-user-v2","zitadel-user-v2beta","zitadel-user-v3alpha","zitadel-v1","zitadel-v1-v1"]
"zitadel-action-v1" = ["zitadel-v1"]
"zitadel-admin-v1" = ["zitadel-event-v1","zitadel-idp-v1","zitadel-instance-v1","zitadel-management-v1","zitadel-member-v1","zitadel-milestone-v1","zitadel-org-v1","zitadel-policy-v1","zitadel-settings-v1","zitadel-text-v1","zitadel-v1","zitadel-v1-v1"]
"zitadel-app-v1" = ["zitadel-v1"]
"zitadel-auth-v1" = ["zitadel-change-v1","zitadel-idp-v1","zitadel-metadata-v1","zitadel-org-v1","zitadel-policy-v1","zitadel-user-v1","zitadel-v1"]
"zitadel-authn-v1" = ["zitadel-v1"]
"zitadel-change-v1" = ["zitadel-v1"]
"zitadel-event-v1" = ["zitadel-v1"]
"zitadel-feature-v1" = []
"zitadel-feature-v2" = ["zitadel-object-v2"]
"zitadel-feature-v2beta" = ["zitadel-object-v2beta"]
"zitadel-idp-v1" = ["zitadel-v1"]
"zitadel-idp-v2" = ["zitadel-object-v2"]
"zitadel-instance-v1" = ["zitadel-v1"]
"zitadel-management-v1" = ["zitadel-action-v1","zitadel-app-v1","zitadel-authn-v1","zitadel-change-v1","zitadel-idp-v1","zitadel-member-v1","zitadel-metadata-v1","zitadel-org-v1","zitadel-policy-v1","zitadel-project-v1","zitadel-text-v1","zitadel-user-v1","zitadel-v1"]
"zitadel-member-v1" = ["zitadel-user-v1","zitadel-v1"]
"zitadel-metadata-v1" = ["zitadel-v1"]
"zitadel-milestone-v1" = []
"zitadel-object-v2" = []
"zitadel-object-v2beta" = []
"zitadel-object-v3alpha" = []
"zitadel-oidc-v2" = ["zitadel-object-v2"]
"zitadel-oidc-v2beta" = ["zitadel-object-v2beta"]
"zitadel-org-v1" = ["zitadel-v1"]
"zitadel-org-v2" = ["zitadel-object-v2"]
"zitadel-org-v2beta" = ["zitadel-object-v2beta"]
"zitadel-policy-v1" = ["zitadel-idp-v1","zitadel-v1"]
"zitadel-project-v1" = ["zitadel-v1"]
"zitadel-protoc_gen_zitadel-v2" = []
"zitadel-quota-v1" = []
"zitadel-resources-action-v3alpha" = ["zitadel-object-v3alpha","zitadel-resources-object-v3alpha"]
"zitadel-resources-object-v3alpha" = ["zitadel-object-v3alpha"]
"zitadel-resources-webkey-v3alpha" = ["zitadel-object-v3alpha","zitadel-resources-object-v3alpha"]
"zitadel-session-v2" = ["zitadel-object-v2","zitadel-v1"]
"zitadel-session-v2beta" = ["zitadel-object-v2beta","zitadel-v1"]
"zitadel-settings-object-v3alpha" = ["zitadel-object-v3alpha"]
"zitadel-settings-v1" = ["zitadel-v1"]
"zitadel-settings-v2" = ["zitadel-object-v2"]
"zitadel-settings-v2beta" = ["zitadel-object-v2beta"]
"zitadel-system-v1" = ["zitadel-feature-v1","zitadel-instance-v1","zitadel-member-v1","zitadel-quota-v1","zitadel-v1"]
"zitadel-text-v1" = ["zitadel-v1"]
"zitadel-user-schema-v3alpha" = ["zitadel-object-v2"]
"zitadel-user-v1" = ["zitadel-v1"]
"zitadel-user-v2" = ["zitadel-object-v2"]
"zitadel-user-v2beta" = ["zitadel-object-v2beta"]
"zitadel-user-v3alpha" = ["zitadel-object-v2"]
"zitadel-v1" = []
"zitadel-v1-v1" = ["zitadel-authn-v1","zitadel-idp-v1","zitadel-management-v1","zitadel-org-v1","zitadel-v1"]
# @@protoc_insertion_point(features)

[dependencies]
actix-web = { version = "4.5.1", optional = true }
async-trait = { version = "0.1.80", optional = true }
Expand All @@ -64,9 +147,9 @@ custom_error = "1.9.2"
document-features = { version = "0.2.8", optional = true }
jsonwebtoken = { version = "9.3.0", optional = true }
openidconnect = { version = "3.5.0", optional = true }
pbjson-types = { version = "0.6", optional = true }
prost = { version = "0.12.4", optional = true }
prost-types = { version = "0.12.4", optional = true }
pbjson-types = { version = "0.7.0", optional = true }
prost = { version = "0.13.1", optional = true }
prost-types = { version = "0.13.1", optional = true }
reqwest = { version = "0.11.27", features = ["json", "rustls-tls"], default-features = false, optional = true }
rocket = { version = "0.5.0", optional = true }
serde = { version = "1.0.200", features = ["derive"], optional = true }
Expand All @@ -77,17 +160,16 @@ tokio = { version = "1.37.0", optional = true, features = [
"macros",
"rt-multi-thread",
] }
tonic = { version = "0.11", features = [
tonic = { version = "0.12.1", features = [
"tls",
"tls-roots",
"tls-roots-common",
], optional = true }
tonic-types = { version = "0.11", optional = true }
tonic-types = { version = "0.12.1", optional = true }

[dev-dependencies]
chrono = "0.4.38"
tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread"] }
tower = { version = "0.4.13" }

[package.metadata.docs.rs]
all-features = true
all-features = true
6 changes: 3 additions & 3 deletions buf.gen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ plugins:
- extern_path=.google.protobuf=::pbjson_types
- no_server
- name: prost-crate
out: src/api/generated
out: .
strategy: all
opt:
- no_features
- include_file=mod.rs
- gen_crate
- include_file=src/api/generated/mod.rs
57 changes: 45 additions & 12 deletions src/api/clients.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,44 @@ use std::error::Error;
use custom_error::custom_error;
use tonic::codegen::InterceptedService;
use tonic::service::Interceptor;
use tonic::transport::{Channel, Endpoint};
use tonic::transport::{Channel, ClientTlsConfig, Endpoint};
use tonic::{Request, Status};

#[cfg(feature = "interceptors")]
use crate::api::interceptors::{AccessTokenInterceptor, ServiceAccountInterceptor};
use crate::api::zitadel::oidc::v2beta::oidc_service_client::OidcServiceClient;
use crate::api::zitadel::org::v2beta::organization_service_client::OrganizationServiceClient;
use crate::api::zitadel::session::v2beta::session_service_client::SessionServiceClient;
use crate::api::zitadel::settings::v2beta::settings_service_client::SettingsServiceClient;

#[cfg(feature = "api-oidc-v2")]
use crate::api::zitadel::oidc::v2::oidc_service_client::OidcServiceClient;
#[cfg(feature = "api-org-v2")]
use crate::api::zitadel::org::v2::organization_service_client::OrganizationServiceClient;
#[cfg(feature = "api-session-v2")]
use crate::api::zitadel::session::v2::session_service_client::SessionServiceClient;
#[cfg(feature = "api-settings-v2")]
use crate::api::zitadel::settings::v2::settings_service_client::SettingsServiceClient;
#[cfg(feature = "api-user-v2")]
use crate::api::zitadel::user::v2::user_service_client::UserServiceClient;

#[cfg(feature = "api-admin-v1")]
use crate::api::zitadel::admin::v1::admin_service_client::AdminServiceClient;
#[cfg(feature = "api-auth-v1")]
use crate::api::zitadel::auth::v1::auth_service_client::AuthServiceClient;
#[cfg(feature = "api-management-v1")]
use crate::api::zitadel::management::v1::management_service_client::ManagementServiceClient;
#[cfg(feature = "api-system-v1")]
use crate::api::zitadel::system::v1::system_service_client::SystemServiceClient;
use crate::api::zitadel::user::v2beta::user_service_client::UserServiceClient;
use crate::credentials::{AuthenticationOptions, ServiceAccount};

use super::zitadel::{
admin::v1::admin_service_client::AdminServiceClient,
auth::v1::auth_service_client::AuthServiceClient,
management::v1::management_service_client::ManagementServiceClient,
};
#[cfg(feature = "interceptors")]
use crate::credentials::{AuthenticationOptions, ServiceAccount};

custom_error! {
/// Errors that may occur when creating a client.
pub ClientError
InvalidUrl = "the provided url is invalid",
ConnectionError = "could not connect to provided endpoint",
TlsInitializationError = "could not setup tls connection",
}

#[cfg(feature = "interceptors")]
enum AuthType {
None,
AccessToken(String),
Expand All @@ -56,6 +69,7 @@ impl ChainedInterceptor {
}
}

#[cfg(feature = "interceptors")]
pub(crate) fn add_interceptor(mut self, interceptor: Box<dyn Interceptor + Send>) -> Self {
self.interceptors.push(interceptor);
self
Expand All @@ -77,6 +91,7 @@ impl Interceptor for ChainedInterceptor {
/// an authentication method.
pub struct ClientBuilder {
api_endpoint: String,
#[cfg(feature = "interceptors")]
auth_type: AuthType,
}

Expand All @@ -85,6 +100,7 @@ impl ClientBuilder {
pub fn new(api_endpoint: &str) -> Self {
Self {
api_endpoint: api_endpoint.to_string(),
#[cfg(feature = "interceptors")]
auth_type: AuthType::None,
}
}
Expand Down Expand Up @@ -127,6 +143,7 @@ impl ClientBuilder {
/// This function returns a [`ClientError`] if the provided API endpoint
/// cannot be parsed into a valid URL or if the connection to the endpoint
/// is not possible.
#[cfg(feature = "api-admin-v1")]
pub async fn build_admin_client(
&self,
) -> Result<AdminServiceClient<InterceptedService<Channel, ChainedInterceptor>>, Box<dyn Error>>
Expand All @@ -148,6 +165,7 @@ impl ClientBuilder {
/// This function returns a [`ClientError`] if the provided API endpoint
/// cannot be parsed into a valid URL or if the connection to the endpoint
/// is not possible.
#[cfg(feature = "api-auth-v1")]
pub async fn build_auth_client(
&self,
) -> Result<AuthServiceClient<InterceptedService<Channel, ChainedInterceptor>>, Box<dyn Error>>
Expand All @@ -169,6 +187,7 @@ impl ClientBuilder {
/// This function returns a [`ClientError`] if the provided API endpoint
/// cannot be parsed into a valid URL or if the connection to the endpoint
/// is not possible.
#[cfg(feature = "api-management-v1")]
pub async fn build_management_client(
&self,
) -> Result<
Expand All @@ -192,6 +211,7 @@ impl ClientBuilder {
/// This function returns a [`ClientError`] if the provided API endpoint
/// cannot be parsed into a valid URL or if the connection to the endpoint
/// is not possible.
#[cfg(feature = "api-oidc-v2")]
pub async fn build_oidc_client(
&self,
) -> Result<OidcServiceClient<InterceptedService<Channel, ChainedInterceptor>>, Box<dyn Error>>
Expand All @@ -213,6 +233,7 @@ impl ClientBuilder {
/// This function returns a [`ClientError`] if the provided API endpoint
/// cannot be parsed into a valid URL or if the connection to the endpoint
/// is not possible.
#[cfg(feature = "api-org-v2")]
pub async fn build_organization_client(
&self,
) -> Result<
Expand All @@ -236,6 +257,7 @@ impl ClientBuilder {
/// This function returns a [`ClientError`] if the provided API endpoint
/// cannot be parsed into a valid URL or if the connection to the endpoint
/// is not possible.
#[cfg(feature = "api-session-v2")]
pub async fn build_session_client(
&self,
) -> Result<SessionServiceClient<InterceptedService<Channel, ChainedInterceptor>>, Box<dyn Error>>
Expand All @@ -257,6 +279,7 @@ impl ClientBuilder {
/// This function returns a [`ClientError`] if the provided API endpoint
/// cannot be parsed into a valid URL or if the connection to the endpoint
/// is not possible.
#[cfg(feature = "api-settings-v2")]
pub async fn build_settings_client(
&self,
) -> Result<
Expand All @@ -280,6 +303,7 @@ impl ClientBuilder {
/// This function returns a [`ClientError`] if the provided API endpoint
/// cannot be parsed into a valid URL or if the connection to the endpoint
/// is not possible.
#[cfg(feature = "api-system-v1")]
pub async fn build_system_client(
&self,
) -> Result<SystemServiceClient<InterceptedService<Channel, ChainedInterceptor>>, Box<dyn Error>>
Expand All @@ -301,6 +325,7 @@ impl ClientBuilder {
/// This function returns a [`ClientError`] if the provided API endpoint
/// cannot be parsed into a valid URL or if the connection to the endpoint
/// is not possible.
#[cfg(feature = "api-user-v2")]
pub async fn build_user_client(
&self,
) -> Result<UserServiceClient<InterceptedService<Channel, ChainedInterceptor>>, Box<dyn Error>>
Expand All @@ -313,7 +338,9 @@ impl ClientBuilder {
}

fn get_chained_interceptor(&self) -> ChainedInterceptor {
#[allow(unused_mut)]
let mut interceptor = ChainedInterceptor::new();
#[cfg(feature = "interceptors")]
match &self.auth_type {
AuthType::AccessToken(token) => {
interceptor =
Expand All @@ -337,6 +364,12 @@ impl ClientBuilder {
async fn get_channel(api_endpoint: &str) -> Result<Channel, ClientError> {
Endpoint::from_shared(api_endpoint.to_string())
.map_err(|_| ClientError::InvalidUrl)?
.tls_config(
ClientTlsConfig::default()
.assume_http2(true)
.with_native_roots(),
)
.map_err(|_| ClientError::TlsInitializationError)?
.connect()
.await
.map_err(|_| ClientError::ConnectionError)
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

#[cfg(feature = "actix")]
pub mod actix;
#[cfg(feature = "api")]
#[cfg(feature = "api-common")]
pub mod api;
#[cfg(feature = "axum")]
pub mod axum;
Expand Down
2 changes: 1 addition & 1 deletion src/oidc/introspection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ custom_error! {
/// by default and [additional claims](https://zitadel.com/docs/apis/openidoauth/claims)
/// if requested by scope:
/// - When scope contains `urn:zitadel:iam:user:resourceowner`, the fields prefixed with
/// `resource_owner_` are set.
/// `resource_owner_` are set.
/// - When scope contains `urn:zitadel:iam:user:metadata`, the metadata hashmap will be
/// filled with the user metadata.
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
Expand Down

0 comments on commit d1528a2

Please sign in to comment.