gsm_core/messaging_card/
spec.rs1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use crate::messaging_card::ir::MessageCardIr;
5use crate::messaging_card::types::{MessageCard, OauthCard, OauthPrompt, OauthProvider};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub enum RenderIntent {
10 Card,
11 Auth,
12}
13
14#[derive(Debug, Clone, PartialEq)]
15pub enum RenderSpec {
16 Card(Box<MessageCardIr>),
17 Auth(AuthRenderSpec),
18}
19
20impl RenderSpec {
21 pub fn intent(&self) -> RenderIntent {
22 match self {
23 RenderSpec::Card(_) => RenderIntent::Card,
24 RenderSpec::Auth(_) => RenderIntent::Auth,
25 }
26 }
27
28 pub fn as_card(&self) -> Option<&MessageCardIr> {
29 match self {
30 RenderSpec::Card(ir) => Some(ir.as_ref()),
31 _ => None,
32 }
33 }
34
35 pub fn as_auth(&self) -> Option<&AuthRenderSpec> {
36 match self {
37 RenderSpec::Auth(spec) => Some(spec),
38 _ => None,
39 }
40 }
41}
42
43#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
44pub struct AuthRenderSpec {
45 pub provider: OauthProvider,
46 pub scopes: Vec<String>,
47 pub resource: Option<String>,
48 pub prompt: Option<OauthPrompt>,
49 pub metadata: Option<Value>,
50 pub start_url: Option<String>,
51 pub connection_name: Option<String>,
52 pub fallback_button: FallbackButton,
53}
54
55impl AuthRenderSpec {
56 pub fn from_card(card: &MessageCard, oauth: &OauthCard) -> Self {
57 let fallback_title = card
58 .title
59 .clone()
60 .unwrap_or_else(|| format!("Sign in with {}", oauth.provider.display_name()));
61 let fallback_button = FallbackButton {
62 title: fallback_title,
63 url: oauth.start_url.clone(),
64 };
65 Self {
66 provider: oauth.provider.clone(),
67 scopes: oauth.scopes.clone(),
68 resource: oauth.resource.clone(),
69 prompt: oauth.prompt.clone(),
70 metadata: oauth.metadata.clone(),
71 start_url: oauth.start_url.clone(),
72 connection_name: oauth.connection_name.clone(),
73 fallback_button,
74 }
75 }
76}
77
78#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
79pub struct FallbackButton {
80 pub title: String,
81 pub url: Option<String>,
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87 use crate::messaging_card::types::{MessageCardKind, OauthPrompt};
88
89 #[test]
90 fn auth_spec_defaults_title() {
91 let oauth = OauthCard {
92 provider: OauthProvider::Microsoft,
93 scopes: vec!["User.Read".into()],
94 resource: None,
95 prompt: Some(OauthPrompt::Consent),
96 start_url: Some("https://auth/start".into()),
97 connection_name: Some("graph".into()),
98 metadata: None,
99 };
100 let card = MessageCard {
101 kind: MessageCardKind::Oauth,
102 title: None,
103 text: None,
104 footer: None,
105 images: Vec::new(),
106 actions: Vec::new(),
107 allow_markdown: true,
108 #[cfg(feature = "adaptive-cards")]
109 adaptive: None,
110 oauth: Some(oauth.clone()),
111 };
112
113 let spec = AuthRenderSpec::from_card(&card, card.oauth.as_ref().unwrap());
114 assert_eq!(spec.provider, OauthProvider::Microsoft);
115 assert_eq!(spec.scopes, vec!["User.Read".to_string()]);
116 assert_eq!(
117 spec.fallback_button.title,
118 "Sign in with Microsoft".to_string()
119 );
120 assert_eq!(
121 spec.fallback_button.url,
122 Some("https://auth/start".to_string())
123 );
124 assert_eq!(spec.connection_name.as_deref(), Some("graph"));
125 assert_eq!(spec.prompt, Some(OauthPrompt::Consent));
126 }
127}