Skip to content

Commit 8162668

Browse files
feat(core): diesel models and db interface changes for authentication table (#3859)
1 parent 34f7705 commit 8162668

File tree

10 files changed

+542
-0
lines changed

10 files changed

+542
-0
lines changed

crates/common_enums/src/enums.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2172,6 +2172,91 @@ pub enum ApplePayFlow {
21722172
Manual,
21732173
}
21742174

2175+
#[derive(
2176+
Clone,
2177+
Debug,
2178+
Eq,
2179+
Default,
2180+
Hash,
2181+
PartialEq,
2182+
serde::Deserialize,
2183+
serde::Serialize,
2184+
strum::Display,
2185+
strum::EnumString,
2186+
utoipa::ToSchema,
2187+
Copy,
2188+
)]
2189+
#[router_derive::diesel_enum(storage_type = "text")]
2190+
#[serde(rename_all = "snake_case")]
2191+
#[strum(serialize_all = "snake_case")]
2192+
pub enum AuthenticationStatus {
2193+
#[default]
2194+
Started,
2195+
Pending,
2196+
Success,
2197+
Failed,
2198+
}
2199+
2200+
impl AuthenticationStatus {
2201+
pub fn is_terminal_status(&self) -> bool {
2202+
match self {
2203+
Self::Started | Self::Pending => false,
2204+
Self::Success | Self::Failed => true,
2205+
}
2206+
}
2207+
2208+
pub fn is_failed(&self) -> bool {
2209+
self == &Self::Failed
2210+
}
2211+
}
2212+
2213+
#[derive(
2214+
Clone,
2215+
Debug,
2216+
Eq,
2217+
Default,
2218+
Hash,
2219+
PartialEq,
2220+
serde::Deserialize,
2221+
serde::Serialize,
2222+
strum::Display,
2223+
strum::EnumString,
2224+
utoipa::ToSchema,
2225+
Copy,
2226+
)]
2227+
#[router_derive::diesel_enum(storage_type = "text")]
2228+
#[serde(rename_all = "snake_case")]
2229+
#[strum(serialize_all = "snake_case")]
2230+
pub enum DecoupledAuthenticationType {
2231+
#[default]
2232+
Challenge,
2233+
Frictionless,
2234+
}
2235+
2236+
#[derive(
2237+
Clone,
2238+
Debug,
2239+
Eq,
2240+
Default,
2241+
Hash,
2242+
PartialEq,
2243+
serde::Deserialize,
2244+
serde::Serialize,
2245+
strum::Display,
2246+
strum::EnumString,
2247+
utoipa::ToSchema,
2248+
Copy,
2249+
)]
2250+
#[router_derive::diesel_enum(storage_type = "text")]
2251+
#[serde(rename_all = "snake_case")]
2252+
#[strum(serialize_all = "snake_case")]
2253+
pub enum AuthenticationLifecycleStatus {
2254+
Used,
2255+
#[default]
2256+
Unused,
2257+
Expired,
2258+
}
2259+
21752260
#[derive(
21762261
Clone,
21772262
Copy,
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
use diesel::{AsChangeset, Identifiable, Insertable, Queryable};
2+
use serde::{self, Deserialize, Serialize};
3+
use serde_json;
4+
5+
use crate::schema::authentication;
6+
7+
#[derive(Clone, Debug, Eq, PartialEq, Identifiable, Queryable, Serialize, Deserialize)]
8+
#[diesel(table_name = authentication, primary_key(authentication_id))]
9+
pub struct Authentication {
10+
pub authentication_id: String,
11+
pub merchant_id: String,
12+
pub authentication_connector: String,
13+
pub connector_authentication_id: Option<String>,
14+
pub authentication_data: Option<serde_json::Value>,
15+
pub payment_method_id: String,
16+
pub authentication_type: Option<common_enums::DecoupledAuthenticationType>,
17+
pub authentication_status: common_enums::AuthenticationStatus,
18+
pub authentication_lifecycle_status: common_enums::AuthenticationLifecycleStatus,
19+
#[serde(with = "common_utils::custom_serde::iso8601")]
20+
pub created_at: time::PrimitiveDateTime,
21+
#[serde(with = "common_utils::custom_serde::iso8601")]
22+
pub modified_at: time::PrimitiveDateTime,
23+
pub error_message: Option<String>,
24+
pub error_code: Option<String>,
25+
pub connector_metadata: Option<serde_json::Value>,
26+
}
27+
28+
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Insertable)]
29+
#[diesel(table_name = authentication)]
30+
pub struct AuthenticationNew {
31+
pub authentication_id: String,
32+
pub merchant_id: String,
33+
pub authentication_connector: String,
34+
pub connector_authentication_id: Option<String>,
35+
pub authentication_data: Option<serde_json::Value>,
36+
pub payment_method_id: String,
37+
pub authentication_type: Option<common_enums::DecoupledAuthenticationType>,
38+
pub authentication_status: common_enums::AuthenticationStatus,
39+
pub authentication_lifecycle_status: common_enums::AuthenticationLifecycleStatus,
40+
pub error_message: Option<String>,
41+
pub error_code: Option<String>,
42+
pub connector_metadata: Option<serde_json::Value>,
43+
}
44+
45+
#[derive(Debug)]
46+
pub enum AuthenticationUpdate {
47+
AuthenticationDataUpdate {
48+
authentication_data: Option<serde_json::Value>,
49+
connector_authentication_id: Option<String>,
50+
payment_method_id: Option<String>,
51+
authentication_type: Option<common_enums::DecoupledAuthenticationType>,
52+
authentication_status: Option<common_enums::AuthenticationStatus>,
53+
authentication_lifecycle_status: Option<common_enums::AuthenticationLifecycleStatus>,
54+
connector_metadata: Option<serde_json::Value>,
55+
},
56+
PostAuthorizationUpdate {
57+
authentication_lifecycle_status: common_enums::AuthenticationLifecycleStatus,
58+
},
59+
ErrorUpdate {
60+
error_message: Option<String>,
61+
error_code: Option<String>,
62+
authentication_status: common_enums::AuthenticationStatus,
63+
connector_authentication_id: Option<String>,
64+
},
65+
}
66+
67+
#[derive(Clone, Debug, Eq, PartialEq, AsChangeset, Serialize, Deserialize)]
68+
#[diesel(table_name = authentication)]
69+
pub struct AuthenticationUpdateInternal {
70+
pub connector_authentication_id: Option<String>,
71+
pub authentication_data: Option<serde_json::Value>,
72+
pub payment_method_id: Option<String>,
73+
pub authentication_type: Option<common_enums::DecoupledAuthenticationType>,
74+
pub authentication_status: Option<common_enums::AuthenticationStatus>,
75+
pub authentication_lifecycle_status: Option<common_enums::AuthenticationLifecycleStatus>,
76+
pub modified_at: time::PrimitiveDateTime,
77+
pub error_message: Option<String>,
78+
pub error_code: Option<String>,
79+
pub connector_metadata: Option<serde_json::Value>,
80+
}
81+
82+
impl AuthenticationUpdateInternal {
83+
pub fn apply_changeset(self, source: Authentication) -> Authentication {
84+
let Self {
85+
connector_authentication_id,
86+
authentication_data,
87+
payment_method_id,
88+
authentication_type,
89+
authentication_status,
90+
authentication_lifecycle_status,
91+
modified_at: _,
92+
error_code,
93+
error_message,
94+
connector_metadata,
95+
} = self;
96+
Authentication {
97+
connector_authentication_id: connector_authentication_id
98+
.or(source.connector_authentication_id),
99+
authentication_data: authentication_data.or(source.authentication_data),
100+
payment_method_id: payment_method_id.unwrap_or(source.payment_method_id),
101+
authentication_type: authentication_type.or(source.authentication_type),
102+
authentication_status: authentication_status.unwrap_or(source.authentication_status),
103+
authentication_lifecycle_status: authentication_lifecycle_status
104+
.unwrap_or(source.authentication_lifecycle_status),
105+
modified_at: common_utils::date_time::now(),
106+
error_code: error_code.or(source.error_code),
107+
error_message: error_message.or(source.error_message),
108+
connector_metadata: connector_metadata.or(source.connector_metadata),
109+
..source
110+
}
111+
}
112+
}
113+
114+
impl From<AuthenticationUpdate> for AuthenticationUpdateInternal {
115+
fn from(auth_update: AuthenticationUpdate) -> Self {
116+
match auth_update {
117+
AuthenticationUpdate::AuthenticationDataUpdate {
118+
authentication_data,
119+
connector_authentication_id,
120+
authentication_type,
121+
authentication_status,
122+
payment_method_id,
123+
authentication_lifecycle_status,
124+
connector_metadata,
125+
} => Self {
126+
authentication_data,
127+
connector_authentication_id,
128+
authentication_type,
129+
authentication_status,
130+
authentication_lifecycle_status,
131+
modified_at: common_utils::date_time::now(),
132+
payment_method_id,
133+
error_message: None,
134+
error_code: None,
135+
connector_metadata,
136+
},
137+
AuthenticationUpdate::ErrorUpdate {
138+
error_message,
139+
error_code,
140+
authentication_status,
141+
connector_authentication_id,
142+
} => Self {
143+
error_code,
144+
error_message,
145+
authentication_status: Some(authentication_status),
146+
authentication_data: None,
147+
connector_authentication_id,
148+
authentication_type: None,
149+
authentication_lifecycle_status: None,
150+
modified_at: common_utils::date_time::now(),
151+
payment_method_id: None,
152+
connector_metadata: None,
153+
},
154+
AuthenticationUpdate::PostAuthorizationUpdate {
155+
authentication_lifecycle_status,
156+
} => Self {
157+
connector_authentication_id: None,
158+
authentication_data: None,
159+
payment_method_id: None,
160+
authentication_type: None,
161+
authentication_status: None,
162+
authentication_lifecycle_status: Some(authentication_lifecycle_status),
163+
modified_at: common_utils::date_time::now(),
164+
error_message: None,
165+
error_code: None,
166+
connector_metadata: None,
167+
},
168+
}
169+
}
170+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods};
2+
3+
use super::generics;
4+
use crate::{
5+
authentication::{
6+
Authentication, AuthenticationNew, AuthenticationUpdate, AuthenticationUpdateInternal,
7+
},
8+
errors,
9+
schema::authentication::dsl,
10+
PgPooledConn, StorageResult,
11+
};
12+
13+
impl AuthenticationNew {
14+
pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<Authentication> {
15+
generics::generic_insert(conn, self).await
16+
}
17+
}
18+
19+
impl Authentication {
20+
pub async fn update_by_merchant_id_authentication_id(
21+
conn: &PgPooledConn,
22+
merchant_id: String,
23+
authentication_id: String,
24+
authorization_update: AuthenticationUpdate,
25+
) -> StorageResult<Self> {
26+
match generics::generic_update_with_unique_predicate_get_result::<
27+
<Self as HasTable>::Table,
28+
_,
29+
_,
30+
_,
31+
>(
32+
conn,
33+
dsl::merchant_id
34+
.eq(merchant_id.to_owned())
35+
.and(dsl::authentication_id.eq(authentication_id.to_owned())),
36+
AuthenticationUpdateInternal::from(authorization_update),
37+
)
38+
.await
39+
{
40+
Err(error) => match error.current_context() {
41+
errors::DatabaseError::NotFound => Err(error.attach_printable(
42+
"Authentication with the given Authentication ID does not exist",
43+
)),
44+
errors::DatabaseError::NoFieldsToUpdate => {
45+
generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
46+
conn,
47+
dsl::merchant_id
48+
.eq(merchant_id.to_owned())
49+
.and(dsl::authentication_id.eq(authentication_id.to_owned())),
50+
)
51+
.await
52+
}
53+
_ => Err(error),
54+
},
55+
result => result,
56+
}
57+
}
58+
59+
pub async fn find_by_merchant_id_authentication_id(
60+
conn: &PgPooledConn,
61+
merchant_id: &str,
62+
authentication_id: &str,
63+
) -> StorageResult<Self> {
64+
generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
65+
conn,
66+
dsl::merchant_id
67+
.eq(merchant_id.to_owned())
68+
.and(dsl::authentication_id.eq(authentication_id.to_owned())),
69+
)
70+
.await
71+
}
72+
}

crates/diesel_models/src/schema.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,38 @@ diesel::table! {
5858
}
5959
}
6060

61+
diesel::table! {
62+
use diesel::sql_types::*;
63+
use crate::enums::diesel_exports::*;
64+
65+
authentication (authentication_id) {
66+
#[max_length = 64]
67+
authentication_id -> Varchar,
68+
#[max_length = 64]
69+
merchant_id -> Varchar,
70+
#[max_length = 64]
71+
authentication_connector -> Varchar,
72+
#[max_length = 64]
73+
connector_authentication_id -> Nullable<Varchar>,
74+
authentication_data -> Nullable<Jsonb>,
75+
#[max_length = 64]
76+
payment_method_id -> Varchar,
77+
#[max_length = 64]
78+
authentication_type -> Nullable<Varchar>,
79+
#[max_length = 64]
80+
authentication_status -> Varchar,
81+
#[max_length = 64]
82+
authentication_lifecycle_status -> Varchar,
83+
created_at -> Timestamp,
84+
modified_at -> Timestamp,
85+
#[max_length = 64]
86+
error_message -> Nullable<Varchar>,
87+
#[max_length = 64]
88+
error_code -> Nullable<Varchar>,
89+
connector_metadata -> Nullable<Jsonb>,
90+
}
91+
}
92+
6193
diesel::table! {
6294
use diesel::sql_types::*;
6395
use crate::enums::diesel_exports::*;
@@ -1108,6 +1140,7 @@ diesel::table! {
11081140
diesel::allow_tables_to_appear_in_same_query!(
11091141
address,
11101142
api_keys,
1143+
authentication,
11111144
blocklist,
11121145
blocklist_fingerprint,
11131146
blocklist_lookup,

0 commit comments

Comments
 (0)