Skip to content

Commit 78bcbc3

Browse files
committed
trait implementation
1 parent dc4cd19 commit 78bcbc3

File tree

7 files changed

+94
-26
lines changed

7 files changed

+94
-26
lines changed

sdk/identity/azure_identity/src/azure_cli_credential.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License.
33

44
use crate::{
5+
authentication_error,
56
env::Env,
67
process::{new_executor, shell_exec, Executor, OutputProcessor},
78
validate_scope, validate_subscription, validate_tenant_id,
@@ -158,7 +159,9 @@ impl TokenCredential for AzureCliCredential {
158159

159160
trace!("running Azure CLI command: {command:?}");
160161

161-
shell_exec::<CliTokenResponse>(self.executor.clone(), &self.env, &command).await
162+
shell_exec::<CliTokenResponse>(self.executor.clone(), &self.env, &command)
163+
.await
164+
.map_err(authentication_error::<Self>)
162165
}
163166
}
164167

sdk/identity/azure_identity/src/azure_developer_cli_credential.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License.
33

44
use crate::{
5+
authentication_error,
56
env::Env,
67
process::{new_executor, shell_exec, Executor, OutputProcessor},
78
validate_scope, validate_tenant_id,
@@ -128,7 +129,9 @@ impl TokenCredential for AzureDeveloperCliCredential {
128129
command.push(" --tenant-id ");
129130
command.push(tenant_id);
130131
}
131-
shell_exec::<AzdTokenResponse>(self.executor.clone(), &self.env, &command).await
132+
shell_exec::<AzdTokenResponse>(self.executor.clone(), &self.env, &command)
133+
.await
134+
.map_err(authentication_error::<Self>)
132135
}
133136
}
134137

sdk/identity/azure_identity/src/azure_pipelines_credential.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ impl fmt::Display for ErrorHeaders {
226226
#[cfg(test)]
227227
mod tests {
228228
use super::*;
229-
use crate::env::Env;
229+
use crate::{env::Env, TroubleshootingGuide, TSG_LINK_ERROR_TEXT};
230230
use azure_core::{
231231
http::{BufResponse, ClientOptions, Transport},
232232
Bytes,
@@ -300,7 +300,10 @@ mod tests {
300300
err.to_string().contains("bar"),
301301
));
302302
assert!(
303-
err.to_string().contains(&format!("{}apc", crate::TSG_LINK)),
303+
err.to_string().contains(&format!(
304+
"{TSG_LINK_ERROR_TEXT}{}",
305+
AzurePipelinesCredential::FRAGMENT
306+
)),
304307
"expected error to contain a link to the troubleshooting guide, got '{err}'",
305308
);
306309
}

sdk/identity/azure_identity/src/client_secret_credential.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ impl TokenCredential for ClientSecretCredential {
172172
#[cfg(test)]
173173
mod tests {
174174
use super::*;
175-
use crate::{tests::*, TSG_LINK};
175+
use crate::{tests::*, TroubleshootingGuide, TSG_LINK_ERROR_TEXT};
176176
use azure_core::{
177177
authority_hosts::AZURE_PUBLIC_CLOUD,
178178
http::{headers::Headers, BufResponse, StatusCode, Transport},
@@ -237,8 +237,10 @@ mod tests {
237237
err
238238
);
239239
assert!(
240-
err.to_string()
241-
.contains(&format!("{TSG_LINK}client-secret")),
240+
err.to_string().contains(&format!(
241+
"{TSG_LINK_ERROR_TEXT}{}",
242+
ClientSecretCredential::FRAGMENT
243+
)),
242244
"expected error to contain a link to the troubleshooting guide, got '{err}'",
243245
);
244246
}

sdk/identity/azure_identity/src/lib.rs

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -112,26 +112,75 @@ fn get_authority_host(env: Option<Env>, option: Option<String>) -> Result<Url> {
112112
Url::parse(&authority_host).map_err(Into::<Error>::into)
113113
}
114114

115-
const TSG_LINK: &str = ". To troubleshoot, visit https://aka.ms/azsdk/rust/identity/troubleshoot#";
115+
const TSG_LINK_ERROR_TEXT: &str =
116+
". To troubleshoot, visit https://aka.ms/azsdk/rust/identity/troubleshoot";
117+
118+
/// A troubleshooting guide entry.
119+
#[doc(hidden)]
120+
pub trait TroubleshootingGuide: private::Sealed {
121+
/// The URL fragment (leading '#' and an anchor) for this type's entry in the troubleshooting guide.
122+
const FRAGMENT: &'static str;
123+
}
124+
125+
#[cfg(not(target_arch = "wasm32"))]
126+
impl TroubleshootingGuide for AzureCliCredential {
127+
const FRAGMENT: &'static str = "#azure-cli";
128+
}
129+
130+
#[cfg(not(target_arch = "wasm32"))]
131+
impl TroubleshootingGuide for AzureDeveloperCliCredential {
132+
const FRAGMENT: &'static str = "#azd";
133+
}
134+
135+
impl TroubleshootingGuide for AzurePipelinesCredential {
136+
const FRAGMENT: &'static str = "#apc";
137+
}
138+
139+
#[cfg(feature = "client_certificate")]
140+
impl TroubleshootingGuide for ClientCertificateCredential {
141+
const FRAGMENT: &'static str = "#client-cert";
142+
}
143+
144+
impl TroubleshootingGuide for ClientSecretCredential {
145+
const FRAGMENT: &'static str = "#client-secret";
146+
}
147+
148+
impl TroubleshootingGuide for ManagedIdentityCredential {
149+
const FRAGMENT: &'static str = "#managed-id";
150+
}
151+
152+
impl TroubleshootingGuide for WorkloadIdentityCredential {
153+
const FRAGMENT: &'static str = "#workload";
154+
}
155+
156+
mod private {
157+
pub trait Sealed {}
158+
#[cfg(not(target_arch = "wasm32"))]
159+
impl Sealed for super::AzureCliCredential {}
160+
#[cfg(not(target_arch = "wasm32"))]
161+
impl Sealed for super::AzureDeveloperCliCredential {}
162+
impl Sealed for super::AzurePipelinesCredential {}
163+
#[cfg(feature = "client_certificate")]
164+
impl Sealed for super::ClientCertificateCredential {}
165+
impl Sealed for super::ClientSecretCredential {}
166+
impl Sealed for super::ManagedIdentityCredential {}
167+
impl Sealed for super::WorkloadIdentityCredential {}
168+
}
116169

117170
/// Map an error from a credential's get_token() method to an ErrorKind::Credential error, appending
118171
/// a link to the troubleshooting guide entry for that credential, if it has one.
119172
///
120173
/// TODO: decide whether to map to ErrorKind::Credential here (https://github.com/Azure/azure-sdk-for-rust/issues/3127)
121-
fn authentication_error<T: 'static>(e: azure_core::Error) -> azure_core::Error {
174+
fn authentication_error<T: TroubleshootingGuide + 'static>(
175+
e: azure_core::Error,
176+
) -> azure_core::Error {
122177
azure_core::Error::with_message_fn(e.kind().clone(), || {
123178
let type_name = std::any::type_name::<T>();
124-
let short_name = type_name.rsplit("::").next().unwrap_or(type_name); // cspell:ignore rsplit
125-
let link = match short_name {
126-
"AzureCliCredential" => format!("{TSG_LINK}azure-cli"),
127-
"AzureDeveloperCliCredential" => format!("{TSG_LINK}azd"),
128-
"AzurePipelinesCredential" => format!("{TSG_LINK}apc"),
129-
#[cfg(feature = "client_certificate")]
130-
"ClientCertificateCredential" => format!("{TSG_LINK}client-cert"),
131-
"ClientSecretCredential" => format!("{TSG_LINK}client-secret"),
132-
"ManagedIdentityCredential" => format!("{TSG_LINK}managed-id"),
133-
"WorkloadIdentityCredential" => format!("{TSG_LINK}workload"),
134-
_ => "".to_string(),
179+
let short_name = type_name.rsplit("::").next().unwrap_or(type_name);
180+
let link = if T::FRAGMENT.is_empty() {
181+
String::new()
182+
} else {
183+
format!("{TSG_LINK_ERROR_TEXT}{}", T::FRAGMENT)
135184
};
136185

137186
format!("{short_name} authentication failed: {e}{link}")

sdk/identity/azure_identity/src/managed_identity_credential.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,11 @@ fn get_source(env: &Env) -> ManagedIdentitySource {
165165
#[cfg(test)]
166166
mod tests {
167167
use super::*;
168-
use crate::env::Env;
169-
use crate::tests::{LIVE_TEST_RESOURCE, LIVE_TEST_SCOPES};
168+
use crate::{
169+
env::Env,
170+
tests::{LIVE_TEST_RESOURCE, LIVE_TEST_SCOPES},
171+
TroubleshootingGuide, TSG_LINK_ERROR_TEXT,
172+
};
170173
use azure_core::http::headers::Headers;
171174
use azure_core::http::{BufResponse, Method, Request, StatusCode, Transport, Url};
172175
use azure_core::time::OffsetDateTime;
@@ -459,8 +462,10 @@ mod tests {
459462
.to_string()
460463
.contains("the requested identity has not been assigned to this resource"));
461464
assert!(
462-
err.to_string()
463-
.contains(&format!("{}managed-id", crate::TSG_LINK)),
465+
err.to_string().contains(&format!(
466+
"{TSG_LINK_ERROR_TEXT}{}",
467+
ManagedIdentityCredential::FRAGMENT
468+
)),
464469
"expected error to contain a link to the troubleshooting guide, got '{err}'",
465470
);
466471
}

sdk/identity/azure_identity/src/workload_identity_credential.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ mod tests {
191191
client_assertion_credential::tests::{is_valid_request, FAKE_ASSERTION},
192192
env::Env,
193193
tests::*,
194+
TroubleshootingGuide, TSG_LINK_ERROR_TEXT,
194195
};
195196
use azure_core::{
196197
http::{
@@ -315,8 +316,10 @@ mod tests {
315316
));
316317
assert!(err.to_string().contains(description));
317318
assert!(
318-
err.to_string()
319-
.contains(&format!("{}workload", crate::TSG_LINK)),
319+
err.to_string().contains(&format!(
320+
"{TSG_LINK_ERROR_TEXT}{}",
321+
WorkloadIdentityCredential::FRAGMENT
322+
)),
320323
"expected error to contain a link to the troubleshooting guide, got '{err}'",
321324
);
322325
}

0 commit comments

Comments
 (0)