mas_handlers/graphql/model/
node.rs1use async_graphql::{ID, Interface};
8use serde::{Deserialize, Serialize};
9use thiserror::Error;
10use ulid::Ulid;
11
12use super::{
13 Anonymous, Authentication, BrowserSession, CompatSession, CompatSsoLogin, OAuth2Client,
14 OAuth2Session, SiteConfig, UpstreamOAuth2Link, UpstreamOAuth2Provider, User, UserEmail,
15 UserEmailAuthentication, UserRecoveryTicket,
16};
17
18#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
19pub enum NodeType {
20 Authentication,
21 BrowserSession,
22 CompatSession,
23 CompatSsoLogin,
24 OAuth2Client,
25 OAuth2Session,
26 UpstreamOAuth2Provider,
27 UpstreamOAuth2Link,
28 User,
29 UserEmail,
30 UserEmailAuthentication,
31 UserRecoveryTicket,
32}
33
34#[derive(Debug, Error)]
35#[error("invalid id")]
36pub enum InvalidID {
37 InvalidFormat,
38 InvalidUlid(#[from] ulid::DecodeError),
39 UnknownPrefix,
40 TypeMismatch { got: NodeType, expected: NodeType },
41}
42
43impl NodeType {
44 fn to_prefix(self) -> &'static str {
45 match self {
46 NodeType::Authentication => "authentication",
47 NodeType::BrowserSession => "browser_session",
48 NodeType::CompatSession => "compat_session",
49 NodeType::CompatSsoLogin => "compat_sso_login",
50 NodeType::OAuth2Client => "oauth2_client",
51 NodeType::OAuth2Session => "oauth2_session",
52 NodeType::UpstreamOAuth2Provider => "upstream_oauth2_provider",
53 NodeType::UpstreamOAuth2Link => "upstream_oauth2_link",
54 NodeType::User => "user",
55 NodeType::UserEmail => "user_email",
56 NodeType::UserEmailAuthentication => "user_email_authentication",
57 NodeType::UserRecoveryTicket => "user_recovery_ticket",
58 }
59 }
60
61 fn from_prefix(prefix: &str) -> Option<Self> {
62 match prefix {
63 "authentication" => Some(NodeType::Authentication),
64 "browser_session" => Some(NodeType::BrowserSession),
65 "compat_session" => Some(NodeType::CompatSession),
66 "compat_sso_login" => Some(NodeType::CompatSsoLogin),
67 "oauth2_client" => Some(NodeType::OAuth2Client),
68 "oauth2_session" => Some(NodeType::OAuth2Session),
69 "upstream_oauth2_provider" => Some(NodeType::UpstreamOAuth2Provider),
70 "upstream_oauth2_link" => Some(NodeType::UpstreamOAuth2Link),
71 "user" => Some(NodeType::User),
72 "user_email" => Some(NodeType::UserEmail),
73 "user_email_authentication" => Some(NodeType::UserEmailAuthentication),
74 "user_recovery_ticket" => Some(NodeType::UserRecoveryTicket),
75 _ => None,
76 }
77 }
78
79 pub fn serialize(self, id: impl Into<Ulid>) -> String {
80 let prefix = self.to_prefix();
81 let id = id.into();
82 format!("{prefix}:{id}")
83 }
84
85 pub fn id(self, id: impl Into<Ulid>) -> ID {
86 ID(self.serialize(id))
87 }
88
89 pub fn deserialize(serialized: &str) -> Result<(Self, Ulid), InvalidID> {
90 let (prefix, id) = serialized.split_once(':').ok_or(InvalidID::InvalidFormat)?;
91 let prefix = NodeType::from_prefix(prefix).ok_or(InvalidID::UnknownPrefix)?;
92 let id = id.parse()?;
93 Ok((prefix, id))
94 }
95
96 pub fn from_id(id: &ID) -> Result<(Self, Ulid), InvalidID> {
97 Self::deserialize(&id.0)
98 }
99
100 pub fn extract_ulid(self, id: &ID) -> Result<Ulid, InvalidID> {
101 let (node_type, ulid) = Self::deserialize(&id.0)?;
102
103 if node_type == self {
104 Ok(ulid)
105 } else {
106 Err(InvalidID::TypeMismatch {
107 got: node_type,
108 expected: self,
109 })
110 }
111 }
112}
113
114#[derive(Interface)]
116#[graphql(field(name = "id", desc = "ID of the object.", ty = "ID"))]
117pub enum Node {
118 Anonymous(Box<Anonymous>),
119 Authentication(Box<Authentication>),
120 BrowserSession(Box<BrowserSession>),
121 CompatSession(Box<CompatSession>),
122 CompatSsoLogin(Box<CompatSsoLogin>),
123 OAuth2Client(Box<OAuth2Client>),
124 OAuth2Session(Box<OAuth2Session>),
125 SiteConfig(Box<SiteConfig>),
126 UpstreamOAuth2Provider(Box<UpstreamOAuth2Provider>),
127 UpstreamOAuth2Link(Box<UpstreamOAuth2Link>),
128 User(Box<User>),
129 UserEmail(Box<UserEmail>),
130 UserEmailAuthentication(Box<UserEmailAuthentication>),
131 UserRecoveryTicket(Box<UserRecoveryTicket>),
132}