Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ff8bf74

Browse files
committed
feature: impl BucketStorageBuilder
1 parent 57d78ce commit ff8bf74

7 files changed

Lines changed: 203 additions & 18 deletions

File tree

.env-template

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,10 @@ SIGN_KEY =
2020
#AFFINE_CLOUD_LOG=debug,mio=off,hyper=off,rustls=off,tantivy=off,sqlx::query=off,jwst_rpc=trace,jwst_rpc::context=off,affine_cloud=trace
2121
#AFFINE_CLOUD_LOG=tower_http=debug,jwst_rpc=trace,jwst_rpc::context=off,affine_cloud=trace
2222
#KECK_LOG=debug,mio=off,hyper=off,rustls=off,tantivy=off,sqlx::query=off,jwst_rpc=trace,jwst_rpc::context=off,keck=trace
23+
24+
# For Bucket storage, BUCKET_ACCESS_TOKEN, BUCKET_SECRET_TOKEN, BUCKET_ENDPOINT is necessary
25+
#BUCKET_ACCESS_TOKEN=
26+
#BUCKET_SECRET_TOKEN=
27+
#BUCKET_ENDPOINT=
28+
#BUCKET_NAME=
29+
#BUCKET_ROOT=

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libs/jwst-storage/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ opendal = { version = "0.37.0", default-features = false, features = [
3333
"rustls",
3434
"services-s3",
3535
] }
36+
dotenvy = "0.15.7"
3637

3738
# ======= workspace dependencies =======
3839
jwst = { workspace = true }

libs/jwst-storage/src/storage/blobs/bucket_local_db.rs

Lines changed: 86 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,19 @@ use crate::JwstStorageError;
55
use bytes::Bytes;
66
use futures::Stream;
77
use jwst::{BlobMetadata, BlobStorage, BucketBlobStorage, JwstResult};
8+
use jwst_storage_migration::Migrator;
9+
use opendal::services::S3;
810
use opendal::Operator;
911
use sea_orm::{DatabaseConnection, EntityTrait};
12+
use sea_orm_migration::MigratorTrait;
1013
use std::collections::HashMap;
1114
use std::sync::Arc;
1215

13-
#[allow(unused)]
1416
pub(super) type BucketBlobModel = <BucketBlobs as EntityTrait>::Model;
15-
#[allow(unused)]
1617
type BucketBlobActiveModel = entities::bucket_blobs::ActiveModel;
17-
#[allow(unused)]
1818
type BucketBlobColumn = <BucketBlobs as EntityTrait>::Column;
1919

2020
#[derive(Clone)]
21-
#[allow(unused)]
2221
pub struct BlobBucketDBStorage {
2322
bucket: Arc<Bucket>,
2423
pub(super) pool: DatabaseConnection,
@@ -31,17 +30,53 @@ impl AsRef<DatabaseConnection> for BlobBucketDBStorage {
3130
}
3231
}
3332

34-
#[allow(unused)]
3533
impl BlobBucketDBStorage {
34+
#[allow(unused)]
3635
pub async fn init_with_pool(
3736
pool: DatabaseConnection,
3837
bucket: Arc<Bucket>,
38+
bucket_storage: Option<BucketStorage>,
3939
) -> JwstStorageResult<Self> {
40-
todo!()
40+
Migrator::up(&pool, None).await?;
41+
Ok(Self {
42+
bucket,
43+
pool,
44+
bucket_storage: bucket_storage.unwrap_or(BucketStorage::new()?),
45+
})
4146
}
4247

43-
pub async fn init_pool(database: &str) -> JwstStorageResult<Self> {
44-
todo!()
48+
#[allow(unused)]
49+
pub async fn init_pool(
50+
database: &str,
51+
bucket_storage: Option<BucketStorage>,
52+
) -> JwstStorageResult<Self> {
53+
let is_sqlite = is_sqlite(database);
54+
let pool = create_connection(database, is_sqlite).await?;
55+
56+
Self::init_with_pool(pool, get_bucket(is_sqlite), bucket_storage).await
57+
}
58+
59+
#[allow(unused)]
60+
async fn all(&self, workspace: &str) -> Result<Vec<BucketBlobModel>, DbErr> {
61+
BucketBlobs::find()
62+
.filter(BucketBlobColumn::Workspace.eq(workspace))
63+
.all(&self.pool)
64+
.await
65+
}
66+
67+
#[allow(unused)]
68+
async fn count(&self, workspace: &str) -> Result<u64, DbErr> {
69+
BucketBlobs::find()
70+
.filter(BucketBlobColumn::Workspace.eq(workspace))
71+
.count(&self.pool)
72+
.await
73+
}
74+
75+
async fn exists(&self, workspace: &str, hash: &str) -> Result<bool, DbErr> {
76+
BucketBlobs::find_by_id((workspace.into(), hash.into()))
77+
.count(&self.pool)
78+
.await
79+
.map(|c| c > 0)
4580
}
4681

4782
pub(super) async fn metadata(
@@ -60,13 +95,6 @@ impl BlobBucketDBStorage {
6095
.and_then(|r| r.ok_or(JwstBlobError::BlobNotFound(hash.into())))
6196
}
6297

63-
async fn exists(&self, workspace: &str, hash: &str) -> Result<bool, DbErr> {
64-
BucketBlobs::find_by_id((workspace.into(), hash.into()))
65-
.count(&self.pool)
66-
.await
67-
.map(|c| c > 0)
68-
}
69-
7098
pub(super) async fn get_blobs_size(&self, workspace: &str) -> Result<Option<i64>, DbErr> {
7199
BucketBlobs::find()
72100
.filter(BucketBlobColumn::Workspace.eq(workspace))
@@ -115,7 +143,29 @@ pub struct BucketStorage {
115143
pub(super) op: Operator,
116144
}
117145

118-
// TODO Builder for BucketStorage;
146+
impl BucketStorage {
147+
#[allow(unused)]
148+
pub fn new() -> JwstStorageResult<Self> {
149+
let access_key = dotenvy::var("BUCKET_ACCESS_TOKEN")?;
150+
let secret_access_key = dotenvy::var("BUCKET_SECRET_TOKEN")?;
151+
let endpoint = dotenvy::var("BUCKET_ENDPOINT")?;
152+
let bucket = dotenvy::var("BUCKET_NAME");
153+
let root = dotenvy::var("BUCKET_ROOT");
154+
155+
let mut builder = S3::default();
156+
157+
builder.bucket(bucket.unwrap_or("default_bucket".to_string()).as_str());
158+
builder.root(root.unwrap_or("default_root".to_string()).as_str());
159+
builder.endpoint(endpoint.as_str());
160+
builder.access_key_id(access_key.as_str());
161+
builder.secret_access_key(secret_access_key.as_str());
162+
163+
Ok(Self {
164+
op: Operator::new(builder)?.finish(),
165+
})
166+
}
167+
}
168+
119169
// TODO add retry layer
120170

121171
#[allow(unused_variables)]
@@ -250,3 +300,23 @@ impl BlobStorage<JwstStorageError> for BlobBucketDBStorage {
250300
return Ok(size.unwrap_or(0));
251301
}
252302
}
303+
304+
#[cfg(test)]
305+
mod tests {
306+
use super::*;
307+
use crate::storage::blobs::utils::BucketStorageBuilder;
308+
309+
#[tokio::test]
310+
#[ignore = "need to config bucket auth"]
311+
async fn test_init_bucket_storage() {
312+
let bucket_storage = BucketStorageBuilder::new()
313+
.bucket("bucket")
314+
.root("root")
315+
.build()
316+
.unwrap();
317+
318+
BlobBucketDBStorage::init_pool("sqlite::memory:", Some(bucket_storage))
319+
.await
320+
.unwrap();
321+
}
322+
}

libs/jwst-storage/src/storage/blobs/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ use thiserror::Error;
1414
use tokio::task::JoinError;
1515
use utils::{ImageParams, InternalBlobMetadata};
1616

17+
pub use utils::BucketStorageBuilder;
18+
pub use bucket_local_db::BlobBucketDBStorage;
19+
1720
#[derive(Debug, Error)]
1821
pub enum JwstBlobError {
1922
#[error("blob not found: {0}")]

libs/jwst-storage/src/storage/blobs/utils.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use crate::storage::blobs::bucket_local_db::BucketStorage;
2+
use crate::{JwstStorageError, JwstStorageResult};
13
use bytes::Bytes;
24
use chrono::{DateTime, Utc};
35
use futures::{
@@ -6,6 +8,8 @@ use futures::{
68
};
79
use image::{load_from_memory, ImageOutputFormat, ImageResult};
810
use jwst::{Base64Engine, BlobMetadata, URL_SAFE_ENGINE};
11+
use opendal::services::S3;
12+
use opendal::Operator;
913
use sea_orm::FromQueryResult;
1014
use sha2::{Digest, Sha256};
1115
use std::{collections::HashMap, io::Cursor};
@@ -139,3 +143,101 @@ impl From<InternalBlobMetadata> for BlobMetadata {
139143
}
140144
}
141145
}
146+
147+
impl TryFrom<HashMap<String, String>> for BucketStorage {
148+
type Error = JwstStorageError;
149+
150+
fn try_from(map: HashMap<String, String>) -> Result<Self, Self::Error> {
151+
let mut builder = BucketStorageBuilder::new();
152+
let access_token = map.get("BUCKET_ACCESS_TOKEN");
153+
let secret_access_key = map.get("BUCKET_SECRET_TOKEN");
154+
let endpoint = map.get("BUCKET_ENDPOINT");
155+
let bucket = map.get("BUCKET_NAME");
156+
let root = map.get("BUCKET_ROOT");
157+
158+
if let Some(access_token) = access_token {
159+
builder = builder.access_key(access_token);
160+
}
161+
if let Some(secret_access_key) = secret_access_key {
162+
builder = builder.secret_access_key(secret_access_key);
163+
}
164+
if let Some(endpoint) = endpoint {
165+
builder = builder.endpoint(endpoint);
166+
}
167+
if let Some(bucket) = bucket {
168+
builder = builder.bucket(bucket);
169+
}
170+
if let Some(root) = root {
171+
builder = builder.root(root);
172+
}
173+
174+
builder.build()
175+
}
176+
}
177+
178+
pub struct BucketStorageBuilder {
179+
access_key: String,
180+
secret_access_key: String,
181+
endpoint: String,
182+
bucket: String,
183+
root: String,
184+
}
185+
186+
impl BucketStorageBuilder {
187+
pub fn new() -> Self {
188+
let access_key =
189+
dotenvy::var("BUCKET_ACCESS_TOKEN").unwrap_or("default_access_key".to_string());
190+
let secret_access_key =
191+
dotenvy::var("BUCKET_SECRET_TOKEN").unwrap_or("default_secret_key".to_string());
192+
let endpoint = dotenvy::var("BUCKET_ENDPOINT").unwrap_or("default_endpoint".to_string());
193+
let bucket = dotenvy::var("BUCKET_NAME").unwrap_or("__default_bucket__".to_string());
194+
let root = dotenvy::var("BUCKET_ROOT").unwrap_or("__default_root__".to_string());
195+
196+
Self {
197+
access_key,
198+
secret_access_key,
199+
endpoint,
200+
bucket,
201+
root,
202+
}
203+
}
204+
205+
pub fn access_key(mut self, access_key: &str) -> Self {
206+
self.access_key = access_key.to_string();
207+
self
208+
}
209+
210+
pub fn secret_access_key(mut self, secret_access_key: &str) -> Self {
211+
self.secret_access_key = secret_access_key.to_string();
212+
self
213+
}
214+
215+
pub fn endpoint(mut self, endpoint: &str) -> Self {
216+
self.endpoint = endpoint.to_string();
217+
self
218+
}
219+
220+
pub fn bucket(mut self, bucket: &str) -> Self {
221+
self.bucket = bucket.to_string();
222+
self
223+
}
224+
225+
pub fn root(mut self, root: &str) -> Self {
226+
self.root = root.to_string();
227+
self
228+
}
229+
230+
pub fn build(self) -> JwstStorageResult<BucketStorage> {
231+
let mut builder = S3::default();
232+
233+
builder.bucket(self.bucket.as_str());
234+
builder.root(self.root.as_str());
235+
builder.endpoint(self.endpoint.as_str());
236+
builder.access_key_id(self.access_key.as_str());
237+
builder.secret_access_key(self.secret_access_key.as_str());
238+
239+
Ok(BucketStorage {
240+
op: Operator::new(builder)?.finish(),
241+
})
242+
}
243+
}

libs/jwst-storage/src/types.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ pub enum JwstStorageError {
1919
#[error("failed to process blob")]
2020
JwstBlob(#[from] crate::storage::blobs::JwstBlobError),
2121
#[error("bucket error")]
22-
JwstBucketError(#[from] opendal::Error),
23-
22+
JwstBucket(#[from] opendal::Error),
23+
#[error("env variables read error")]
24+
DotEnvy(#[from] dotenvy::Error),
2425
}
2526

2627
pub type JwstStorageResult<T> = Result<T, JwstStorageError>;

0 commit comments

Comments
 (0)