feat(db): serialize to JSON on write and deserialize from JSON on read
Previously, our Redis abstraction only worked with strings because it only dealt with the somewhat nebulous concept of message metadata. Now that we also want to use it for concrete types, it makes sense to bake in automatic serialization from and deserialization to those types. This frees consumers from the responsibility of having to serialize to a Redis-friendly format manually. This change achieves that aim by making the db methods generic. `db.set` gets a type parameter that is `Serialize` and the getters get one that is `DeserializeOwned`.
This commit is contained in:
Родитель
6a43ce4048
Коммит
31da83bb39
1
API.md
1
API.md
|
@ -35,6 +35,7 @@
|
|||
- `code: 500, errno: 117`: Db Error
|
||||
- `code: 500, errno: 118`: Not Implemeneted
|
||||
- `code: 500, errno: 119`: HMAC error
|
||||
- `code: 500, errno: 120`: JSON error
|
||||
|
||||
The following errors include additional response properties:
|
||||
|
||||
|
|
|
@ -201,6 +201,10 @@ pub enum AppErrorKind {
|
|||
/// An error occured while hashing a value.
|
||||
#[fail(display = "HMAC error: {}", _0)]
|
||||
HmacError(String),
|
||||
|
||||
/// An error occured while serializing or deserializing JSON.
|
||||
#[fail(display = "JSON error: {}", _0)]
|
||||
JsonError(String),
|
||||
}
|
||||
|
||||
impl AppErrorKind {
|
||||
|
@ -252,6 +256,8 @@ impl AppErrorKind {
|
|||
|
||||
AppErrorKind::HmacError(_) => Some(119),
|
||||
|
||||
AppErrorKind::JsonError(_) => Some(120),
|
||||
|
||||
AppErrorKind::BadRequest
|
||||
| AppErrorKind::NotFound
|
||||
| AppErrorKind::MethodNotAllowed
|
||||
|
|
33
src/db.rs
33
src/db.rs
|
@ -16,6 +16,8 @@ use std::fmt::{self, Display, Formatter};
|
|||
|
||||
use hmac::{crypto_mac::InvalidKeyLength, Hmac, Mac};
|
||||
use redis::{Client as RedisClient, Commands, RedisError};
|
||||
use serde::{de::DeserializeOwned, ser::Serialize};
|
||||
use serde_json;
|
||||
use sha2::Sha256;
|
||||
|
||||
use app_errors::{AppError, AppErrorKind, AppResult};
|
||||
|
@ -45,30 +47,45 @@ impl Client {
|
|||
}
|
||||
|
||||
/// Read data.
|
||||
pub fn get(&self, key: &str, data_type: DataType) -> AppResult<String> {
|
||||
pub fn get<D>(&self, key: &str, data_type: DataType) -> AppResult<D>
|
||||
where
|
||||
D: DeserializeOwned,
|
||||
{
|
||||
let key = self.generate_key(key, data_type)?;
|
||||
self.client.get(key.as_str()).map_err(From::from)
|
||||
self.client
|
||||
.get(key.as_str())
|
||||
.map_err(From::from)
|
||||
.and_then(|value: String| serde_json::from_str(&value).map_err(From::from))
|
||||
}
|
||||
|
||||
/// Read and delete data.
|
||||
pub fn consume(&self, key: &str, data_type: DataType) -> AppResult<String> {
|
||||
pub fn consume<D>(&self, key: &str, data_type: DataType) -> AppResult<D>
|
||||
where
|
||||
D: DeserializeOwned,
|
||||
{
|
||||
let key = self.generate_key(key, data_type)?;
|
||||
let key_str = key.as_str();
|
||||
self.client
|
||||
.get(key_str)
|
||||
.map(|metadata| {
|
||||
.map_err(From::from)
|
||||
.and_then(|value: String| {
|
||||
self.client.del::<&str, u8>(key_str).ok();
|
||||
metadata
|
||||
}).map_err(From::from)
|
||||
serde_json::from_str(&value).map_err(From::from)
|
||||
})
|
||||
}
|
||||
|
||||
/// Store data.
|
||||
///
|
||||
/// Any data previously stored for the key
|
||||
/// will be clobbered.
|
||||
pub fn set(&self, key: &str, data: &str, data_type: DataType) -> AppResult<()> {
|
||||
pub fn set<D>(&self, key: &str, data: &D, data_type: DataType) -> AppResult<()>
|
||||
where
|
||||
D: Serialize,
|
||||
{
|
||||
let key = self.generate_key(key, data_type)?;
|
||||
self.client.set(key.as_str(), data).map_err(From::from)
|
||||
self.client
|
||||
.set(key.as_str(), serde_json::to_string(data)?)
|
||||
.map_err(From::from)
|
||||
}
|
||||
|
||||
fn generate_key(&self, key: &str, data_type: DataType) -> AppResult<String> {
|
||||
|
|
|
@ -41,6 +41,7 @@ impl MessageData {
|
|||
/// Any data previously stored for the message id
|
||||
/// will be replaced.
|
||||
pub fn set(&self, message_id: &str, metadata: &str) -> AppResult<()> {
|
||||
self.client.set(message_id, metadata, DataType::MessageData)
|
||||
self.client
|
||||
.set(message_id, &metadata, DataType::MessageData)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ fn set() {
|
|||
.unwrap();
|
||||
assert!(!key_exists, "unhashed key should not exist in redis");
|
||||
let value: String = test.redis_client.get(test.internal_key.as_str()).unwrap();
|
||||
assert_eq!(value, "wibble");
|
||||
assert_eq!(value, "\"wibble\"");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -222,6 +222,6 @@ impl From<DeleteMessageError> for AppError {
|
|||
|
||||
impl From<JsonError> for AppError {
|
||||
fn from(error: JsonError) -> AppError {
|
||||
AppErrorKind::QueueError(format!("JSON error: {:?}", error)).into()
|
||||
AppErrorKind::JsonError(format!("JSON error: {:?}", error)).into()
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче