Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 8212d24

Browse files
committedMay 11, 2023
fix: Updated to use jsonb type for storing session data
1 parent a725dd2 commit 8212d24

File tree

3 files changed

+64
-50
lines changed

3 files changed

+64
-50
lines changed
 

‎README.md

Whitespace-only changes.

‎src/lib.rs

+61-44
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
use std::collections::HashMap;
2-
use std::sync::Arc;
1+
use crate::ConnectionData::{ConnectionPool, ConnectionString};
32
use actix_session::storage::{LoadError, SaveError, SessionKey, SessionStore, UpdateError};
43
use chrono::Utc;
5-
use sqlx::{Pool, Postgres, Row};
6-
use sqlx::postgres::PgPoolOptions;
74
use rand::{distributions::Alphanumeric, rngs::OsRng, Rng as _};
5+
use serde_json::{self, Value};
6+
use sqlx::postgres::PgPoolOptions;
7+
use sqlx::{Pool, Postgres, Row};
8+
use std::collections::HashMap;
9+
use std::sync::Arc;
810
use time::Duration;
9-
use serde_json;
10-
use crate::ConnectionData::{ConnectionPool, ConnectionString};
1111

1212
/// Use Postgres via Sqlx as session storage backend.
1313
///
@@ -107,25 +107,27 @@ impl SqlxPostgresqlSessionStore {
107107
pub fn builder<S: Into<String>>(connection_string: S) -> SqlxPostgresqlSessionStoreBuilder {
108108
SqlxPostgresqlSessionStoreBuilder {
109109
connection_data: ConnectionString(connection_string.into()),
110-
configuration: CacheConfiguration::default()
110+
configuration: CacheConfiguration::default(),
111111
}
112112
}
113113

114-
pub async fn new<S: Into<String>>(connection_string: S) -> Result<SqlxPostgresqlSessionStore, anyhow::Error> {
114+
pub async fn new<S: Into<String>>(
115+
connection_string: S,
116+
) -> Result<SqlxPostgresqlSessionStore, anyhow::Error> {
115117
Self::builder(connection_string).build().await
116118
}
117119

118120
pub async fn from_pool(pool: Pool<Postgres>) -> SqlxPostgresqlSessionStoreBuilder {
119121
SqlxPostgresqlSessionStoreBuilder {
120122
connection_data: ConnectionPool(pool.clone()),
121-
configuration: CacheConfiguration::default()
123+
configuration: CacheConfiguration::default(),
122124
}
123125
}
124126
}
125127

126128
pub enum ConnectionData {
127129
ConnectionString(String),
128-
ConnectionPool(Pool<Postgres>)
130+
ConnectionPool(Pool<Postgres>),
129131
}
130132

131133
#[must_use]
@@ -137,22 +139,19 @@ pub struct SqlxPostgresqlSessionStoreBuilder {
137139
impl SqlxPostgresqlSessionStoreBuilder {
138140
pub async fn build(self) -> Result<SqlxPostgresqlSessionStore, anyhow::Error> {
139141
match self.connection_data {
140-
ConnectionString(conn_string) => {
141-
PgPoolOptions::new()
142-
.max_connections(1)
143-
.connect(conn_string.as_str())
144-
.await
145-
.map_err(Into::into)
146-
.map(|pool| {
147-
SqlxPostgresqlSessionStore {
148-
client_pool: pool,
149-
configuration: self.configuration
150-
}
151-
})
152-
},
142+
ConnectionString(conn_string) => PgPoolOptions::new()
143+
.max_connections(1)
144+
.connect(conn_string.as_str())
145+
.await
146+
.map_err(Into::into)
147+
.map(|pool| SqlxPostgresqlSessionStore {
148+
client_pool: pool,
149+
configuration: self.configuration,
150+
}),
153151
ConnectionPool(pool) => Ok(SqlxPostgresqlSessionStore {
154-
client_pool: pool, configuration: self.configuration
155-
})
152+
client_pool: pool,
153+
configuration: self.configuration,
154+
}),
156155
}
157156
}
158157
}
@@ -162,61 +161,79 @@ pub(crate) type SessionState = HashMap<String, String>;
162161
impl SessionStore for SqlxPostgresqlSessionStore {
163162
async fn load(&self, session_key: &SessionKey) -> Result<Option<SessionState>, LoadError> {
164163
let key = (self.configuration.cache_keygen)(session_key.as_ref());
165-
let row = sqlx::query("SELECT session_state FROM sessions WHERE key = $1 AND expires > NOW()")
166-
.bind( key)
167-
.fetch_optional(&self.client_pool)
168-
.await
169-
.map_err(Into::into)
170-
.map_err(LoadError::Other)?;
164+
let row =
165+
sqlx::query("SELECT session_state FROM sessions WHERE key = $1 AND expires > NOW()")
166+
.bind(key)
167+
.fetch_optional(&self.client_pool)
168+
.await
169+
.map_err(Into::into)
170+
.map_err(LoadError::Other)?;
171171
match row {
172172
None => Ok(None),
173173
Some(r) => {
174-
let data: String = r.get("session_state");
175-
let state: SessionState = serde_json::from_str(&data).map_err(Into::into).map_err(LoadError::Deserialization)?;
174+
let data: Value = r.get("session_state");
175+
let state: SessionState = serde_json::from_value(data)
176+
.map_err(Into::into)
177+
.map_err(LoadError::Deserialization)?;
176178
Ok(Some(state))
177179
}
178180
}
179181
}
180182

181-
async fn save(&self, session_state: SessionState, ttl: &Duration) -> Result<SessionKey, SaveError> {
182-
let body = serde_json::to_string(&session_state)
183+
async fn save(
184+
&self,
185+
session_state: SessionState,
186+
ttl: &Duration,
187+
) -> Result<SessionKey, SaveError> {
188+
let body = serde_json::to_value(&session_state)
183189
.map_err(Into::into)
184190
.map_err(SaveError::Serialization)?;
185191
let key = generate_session_key();
186192
let cache_key = (self.configuration.cache_keygen)(key.as_ref());
187193
let expires = Utc::now() + chrono::Duration::seconds(ttl.whole_seconds() as i64);
188194
sqlx::query("INSERT INTO sessions(key, session_state, expires) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING")
189195
.bind(cache_key)
190-
.bind( body)
191-
.bind( expires)
196+
.bind(body)
197+
.bind(expires)
192198
.execute(&self.client_pool)
193199
.await
194200
.map_err(Into::into)
195201
.map_err(SaveError::Other)?;
196202
Ok(key)
197203
}
198204

199-
async fn update(&self, session_key: SessionKey, session_state: SessionState, ttl: &Duration) -> Result<SessionKey, UpdateError> {
200-
let body = serde_json::to_string(&session_state).map_err(Into::into).map_err(UpdateError::Serialization)?;
205+
async fn update(
206+
&self,
207+
session_key: SessionKey,
208+
session_state: SessionState,
209+
ttl: &Duration,
210+
) -> Result<SessionKey, UpdateError> {
211+
let body = serde_json::to_value(&session_state)
212+
.map_err(Into::into)
213+
.map_err(UpdateError::Serialization)?;
201214
let cache_key = (self.configuration.cache_keygen)(session_key.as_ref());
202215
let new_expires = Utc::now() + chrono::Duration::seconds(ttl.whole_seconds());
203216
sqlx::query("UPDATE sessions SET session_state = $1, expires = $2 WHERE key = $3")
204-
.bind( body)
205-
.bind( new_expires)
206-
.bind( cache_key)
217+
.bind(body)
218+
.bind(new_expires)
219+
.bind(cache_key)
207220
.execute(&self.client_pool)
208221
.await
209222
.map_err(Into::into)
210223
.map_err(UpdateError::Other)?;
211224
Ok(session_key)
212225
}
213226

214-
async fn update_ttl(&self, session_key: &SessionKey, ttl: &Duration) -> Result<(), anyhow::Error> {
227+
async fn update_ttl(
228+
&self,
229+
session_key: &SessionKey,
230+
ttl: &Duration,
231+
) -> Result<(), anyhow::Error> {
215232
let new_expires = Utc::now() + chrono::Duration::seconds(ttl.whole_seconds() as i64);
216233
let key = (self.configuration.cache_keygen)(session_key.as_ref());
217234
sqlx::query("UPDATE sessions SET expires = $1 WHERE key = $2")
218235
.bind(new_expires)
219-
.bind( key)
236+
.bind(key)
220237
.execute(&self.client_pool)
221238
.await
222239
.map_err(Into::into)

‎tests/session_store.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ pub mod tests {
4040
sqlx::query(
4141
r#"CREATE TABLE sessions(
4242
key TEXT PRIMARY KEY NOT NULL,
43-
session_state TEXT,
43+
session_state JSONB,
4444
expires TIMESTAMP WITH TIME ZONE NOT NULL
4545
);"#,
4646
)
@@ -49,12 +49,9 @@ pub mod tests {
4949
.expect("Could not create table");
5050
let mut session = HashMap::new();
5151
session.insert("key".to_string(), "value".to_string());
52-
let data = postgres_store
53-
.save(session, &time::Duration::days(1))
54-
.await;
52+
let data = postgres_store.save(session, &time::Duration::days(1)).await;
5553
println!("{:#?}", data);
56-
assert!(data
57-
.is_ok());
54+
assert!(data.is_ok());
5855
super::test_helpers::acceptance_test_suite(move || postgres_store.clone(), true).await;
5956
}
6057
}

0 commit comments

Comments
 (0)
Please sign in to comment.