Skip to content

Commit b252ec2

Browse files
committed
trait implementation
1 parent 789bf9d commit b252ec2

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
@@ -167,7 +167,7 @@ impl TokenCredential for ClientSecretCredential {
167167
#[cfg(test)]
168168
mod tests {
169169
use super::*;
170-
use crate::{tests::*, TSG_LINK};
170+
use crate::{tests::*, TroubleshootingGuide, TSG_LINK_ERROR_TEXT};
171171
use azure_core::{
172172
http::{headers::Headers, BufResponse, StatusCode, Transport},
173173
Bytes, Result,
@@ -257,8 +257,10 @@ mod tests {
257257
err
258258
);
259259
assert!(
260-
err.to_string()
261-
.contains(&format!("{TSG_LINK}client-secret")),
260+
err.to_string().contains(&format!(
261+
"{TSG_LINK_ERROR_TEXT}{}",
262+
ClientSecretCredential::FRAGMENT
263+
)),
262264
"expected error to contain a link to the troubleshooting guide, got '{err}'",
263265
);
264266
}

sdk/identity/azure_identity/src/lib.rs

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -133,26 +133,75 @@ fn get_authority_host(env: Option<Env>, cloud: Option<&CloudConfiguration>) -> R
133133
Ok(url)
134134
}
135135

136-
const TSG_LINK: &str = ". To troubleshoot, visit https://aka.ms/azsdk/rust/identity/troubleshoot#";
136+
const TSG_LINK_ERROR_TEXT: &str =
137+
". To troubleshoot, visit https://aka.ms/azsdk/rust/identity/troubleshoot";
138+
139+
/// A troubleshooting guide entry.
140+
#[doc(hidden)]
141+
pub trait TroubleshootingGuide: private::Sealed {
142+
/// The URL fragment (leading '#' and an anchor) for this type's entry in the troubleshooting guide.
143+
const FRAGMENT: &'static str;
144+
}
145+
146+
#[cfg(not(target_arch = "wasm32"))]
147+
impl TroubleshootingGuide for AzureCliCredential {
148+
const FRAGMENT: &'static str = "#azure-cli";
149+
}
150+
151+
#[cfg(not(target_arch = "wasm32"))]
152+
impl TroubleshootingGuide for AzureDeveloperCliCredential {
153+
const FRAGMENT: &'static str = "#azd";
154+
}
155+
156+
impl TroubleshootingGuide for AzurePipelinesCredential {
157+
const FRAGMENT: &'static str = "#apc";
158+
}
159+
160+
#[cfg(feature = "client_certificate")]
161+
impl TroubleshootingGuide for ClientCertificateCredential {
162+
const FRAGMENT: &'static str = "#client-cert";
163+
}
164+
165+
impl TroubleshootingGuide for ClientSecretCredential {
166+
const FRAGMENT: &'static str = "#client-secret";
167+
}
168+
169+
impl TroubleshootingGuide for ManagedIdentityCredential {
170+
const FRAGMENT: &'static str = "#managed-id";
171+
}
172+
173+
impl TroubleshootingGuide for WorkloadIdentityCredential {
174+
const FRAGMENT: &'static str = "#workload";
175+
}
176+
177+
mod private {
178+
pub trait Sealed {}
179+
#[cfg(not(target_arch = "wasm32"))]
180+
impl Sealed for super::AzureCliCredential {}
181+
#[cfg(not(target_arch = "wasm32"))]
182+
impl Sealed for super::AzureDeveloperCliCredential {}
183+
impl Sealed for super::AzurePipelinesCredential {}
184+
#[cfg(feature = "client_certificate")]
185+
impl Sealed for super::ClientCertificateCredential {}
186+
impl Sealed for super::ClientSecretCredential {}
187+
impl Sealed for super::ManagedIdentityCredential {}
188+
impl Sealed for super::WorkloadIdentityCredential {}
189+
}
137190

138191
/// Map an error from a credential's get_token() method to an ErrorKind::Credential error, appending
139192
/// a link to the troubleshooting guide entry for that credential, if it has one.
140193
///
141194
/// TODO: decide whether to map to ErrorKind::Credential here (https://github.com/Azure/azure-sdk-for-rust/issues/3127)
142-
fn authentication_error<T: 'static>(e: azure_core::Error) -> azure_core::Error {
195+
fn authentication_error<T: TroubleshootingGuide + 'static>(
196+
e: azure_core::Error,
197+
) -> azure_core::Error {
143198
azure_core::Error::with_message_fn(e.kind().clone(), || {
144199
let type_name = std::any::type_name::<T>();
145-
let short_name = type_name.rsplit("::").next().unwrap_or(type_name); // cspell:ignore rsplit
146-
let link = match short_name {
147-
"AzureCliCredential" => format!("{TSG_LINK}azure-cli"),
148-
"AzureDeveloperCliCredential" => format!("{TSG_LINK}azd"),
149-
"AzurePipelinesCredential" => format!("{TSG_LINK}apc"),
150-
#[cfg(feature = "client_certificate")]
151-
"ClientCertificateCredential" => format!("{TSG_LINK}client-cert"),
152-
"ClientSecretCredential" => format!("{TSG_LINK}client-secret"),
153-
"ManagedIdentityCredential" => format!("{TSG_LINK}managed-id"),
154-
"WorkloadIdentityCredential" => format!("{TSG_LINK}workload"),
155-
_ => "".to_string(),
200+
let short_name = type_name.rsplit("::").next().unwrap_or(type_name);
201+
let link = if T::FRAGMENT.is_empty() {
202+
String::new()
203+
} else {
204+
format!("{TSG_LINK_ERROR_TEXT}{}", T::FRAGMENT)
156205
};
157206

158207
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::{
@@ -317,8 +318,10 @@ mod tests {
317318
));
318319
assert!(err.to_string().contains(description));
319320
assert!(
320-
err.to_string()
321-
.contains(&format!("{}workload", crate::TSG_LINK)),
321+
err.to_string().contains(&format!(
322+
"{TSG_LINK_ERROR_TEXT}{}",
323+
WorkloadIdentityCredential::FRAGMENT
324+
)),
322325
"expected error to contain a link to the troubleshooting guide, got '{err}'",
323326
);
324327
}

0 commit comments

Comments
 (0)