Skip to content

Commit

Permalink
Merge pull request #238 from sohosai/develop
Browse files Browse the repository at this point in the history
Deploy 20240416
  • Loading branch information
arata-nvm authored Apr 16, 2024
2 parents 0168df8 + 1cfcfd9 commit 2e77175
Show file tree
Hide file tree
Showing 38 changed files with 628 additions and 149 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

81 changes: 56 additions & 25 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ edition = "2021"
anyhow = "1.0.79"
async_zip = { version = "0.0.17", features = ["chrono", "deflate", "tokio"] }
axum = { version = "0.7.4", features = ["macros", "multipart", "query"] }
axum-extra = { version = "0.9.3", features = ["async-read-body"] }
aws-sdk-s3 = { version = "1.20.0", features = ["rt-tokio"] }
base64 = "0.22.0"
bitflags = "2.4.2"
chrono = { version = "0.4.32", features = ["serde"] }
chrono-tz = "0.9.0"
csv = "1.3.0"
dotenvy = "0.15.7"
emojis = "0.6.1"
Expand All @@ -43,7 +43,7 @@ sqlx = { version = "0.7.3", features = [
] }
thiserror = "1.0.56"
tokio = { version = "1.35.1", features = ["full"] }
tokio-util = { version = "0.7.10", features = ["compat"] }
tokio-util = { version = "0.7.10", features = ["compat", "io"] }
tower = { version = "0.4.13", features = ["util"] }
tower-http = { version = "0.5.1", features = ["cors", "trace"] }
tracing = "0.1.40"
Expand Down
57 changes: 56 additions & 1 deletion crates/sos24-domain/src/entity/file_data.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use std::ffi::OsStr;
use std::path::Path;

use getset::{Getters, Setters};
use thiserror::Error;

Expand Down Expand Up @@ -55,7 +58,37 @@ pub struct DestructedFileData {
}

impl_value_object!(FileId(uuid::Uuid));
impl_value_object!(FileName(String));

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FileName(String);

impl FileName {
pub fn sanitized(name: String) -> Self {
// ref: https://github.com/rwf2/Rocket/blob/60f3cd57b06243beaee87fd5b7545e3bf0fa6f60/core/lib/src/fs/file_name.rs#L140-L146
static BAD_CHARS: &[char] = &[
// These have special meaning in a file name.
'.', '/', '\\', // These are treated specially by shells.
'<', '>', '|', ':', '(', ')', '&', ';', '#', '?', '*',
];

let file_name = Path::new(&name)
.file_name()
.and_then(OsStr::to_str)
.and_then(|n| n.split(BAD_CHARS).find(|s| !s.is_empty()))
.unwrap_or("");

let ext = Path::new(&name)
.extension()
.and_then(OsStr::to_str)
.unwrap_or("");

Self(format!("{}.{}", file_name, ext))
}

pub fn value(self) -> String {
self.0
}
}

#[derive(Debug, Error)]
pub enum FileIdError {
Expand All @@ -71,3 +104,25 @@ impl TryFrom<String> for FileId {
Ok(Self(uuid))
}
}

#[cfg(test)]
mod tests {
use crate::entity::file_data::FileName;

#[test]
fn filename_sanitized() {
const TEST_CASES: [(&str, &str); 6] = [
("foo.txt", "foo.txt"),
("foo.exe.txt", "foo.txt"),
("../../foo.txt", "foo.txt"),
("./foo.txt", "foo.txt"),
("/bar/foo.txt", "foo.txt"),
("/bar/.foo.txt", "foo.txt"),
];

for (input, expected) in TEST_CASES {
let actual = FileName::sanitized(String::from(input));
assert_eq!(actual.value().as_str(), expected);
}
}
}
35 changes: 35 additions & 0 deletions crates/sos24-domain/src/entity/file_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,41 @@ impl FileObjectKey {
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ArchiveEntry {
key: FileObjectKey,
filename: FileName,
updated_at: chrono::DateTime<chrono::Utc>,
}

impl ArchiveEntry {
pub fn new(
key: FileObjectKey,
filename: FileName,
updated_at: chrono::DateTime<chrono::Utc>,
) -> Self {
Self {
key,
filename,
updated_at,
}
}

pub fn destruct(self) -> DestructedArchiveEntry {
DestructedArchiveEntry {
key: self.key,
filename: self.filename,
updated_at: self.updated_at,
}
}
}

pub struct DestructedArchiveEntry {
pub key: FileObjectKey,
pub filename: FileName,
pub updated_at: chrono::DateTime<chrono::Utc>,
}

#[cfg(test)]
mod test {
use crate::entity::file_object::generate_content_disposition;
Expand Down
4 changes: 4 additions & 0 deletions crates/sos24-domain/src/entity/form.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,10 @@ impl Form {
self.categories.matches(*project.category())
&& self.attributes.matches(*project.attributes())
}

pub fn find_item(&self, item_id: &FormItemId) -> Option<&FormItem> {
self.items.iter().find(|item| item.id() == item_id)
}
}

impl_value_object!(FormId(uuid::Uuid));
Expand Down
12 changes: 12 additions & 0 deletions crates/sos24-domain/src/entity/form_answer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,18 @@ impl FormAnswer {
self.items = items;
Ok(())
}

pub fn list_file_items(&self) -> Vec<(FormItemId, Vec<FileId>)> {
self.items
.iter()
.filter_map(|item| match &item.kind {
FormAnswerItemKind::File(file) => {
Some((item.item_id().clone(), file.clone().value()))
}
_ => None,
})
.collect()
}
}

impl_value_object!(FormAnswerId(uuid::Uuid));
Expand Down
4 changes: 4 additions & 0 deletions crates/sos24-domain/src/repository/file_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,8 @@ pub trait FileDataRepository: Send + Sync + 'static {
owner_project: ProjectId,
) -> Result<Vec<WithDate<FileData>>, FileDataRepositoryError>;
async fn delete_by_id(&self, id: FileId) -> Result<(), FileDataRepositoryError>;
async fn delete_by_owner_project(
&self,
owner_project: ProjectId,
) -> Result<(), FileDataRepositoryError>;
}
13 changes: 7 additions & 6 deletions crates/sos24-domain/src/repository/file_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use mockall::automock;
use thiserror::Error;
use tokio::io::DuplexStream;

use crate::entity::common::date::WithDate;
use crate::entity::file_data::FileData;
use crate::entity::file_object::{ContentDisposition, FileObject, FileObjectKey, FileSignedUrl};
use crate::entity::file_object::{
ArchiveEntry, ContentDisposition, FileObject, FileObjectKey, FileSignedUrl,
};

#[derive(Debug, Error)]
pub enum FileObjectRepositoryError {
Expand All @@ -27,9 +27,10 @@ pub trait FileObjectRepository: Send + Sync + 'static {
content_disposition: Option<ContentDisposition>,
) -> Result<FileSignedUrl, FileObjectRepositoryError>;
// TODO: 返り値をラッピングしておくと内部仕様が露出しなくてよい
async fn create_archive(
fn create_archive(
&self,
bucket: String,
files: Vec<WithDate<FileData>>,
) -> Result<DuplexStream, FileObjectRepositoryError>;
entry_list: Vec<ArchiveEntry>,
writer: DuplexStream,
) -> impl std::future::Future<Output = Result<(), FileObjectRepositoryError>> + Send;
}
1 change: 1 addition & 0 deletions crates/sos24-domain/src/repository/form_answer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ pub trait FormAnswerRepository: Send + Sync + 'static {
form_id: FormId,
) -> Result<Option<WithDate<FormAnswer>>, FormAnswerRepositoryError>;
async fn update(&self, form_answer: FormAnswer) -> Result<(), FormAnswerRepositoryError>;
async fn delete_by_project_id(&self, id: ProjectId) -> Result<(), FormAnswerRepositoryError>;
}
2 changes: 2 additions & 0 deletions crates/sos24-domain/src/repository/invitation.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use mockall::automock;
use thiserror::Error;

use crate::entity::project::ProjectId;
use crate::entity::user::UserId;
use crate::entity::{
common::date::WithDate,
Expand Down Expand Up @@ -30,4 +31,5 @@ pub trait InvitationRepository: Send + Sync + 'static {

async fn update(&self, invitation: Invitation) -> Result<(), InvitationRepositoryError>;
async fn delete_by_id(&self, id: InvitationId) -> Result<(), InvitationRepositoryError>;
async fn delete_by_project_id(&self, id: ProjectId) -> Result<(), InvitationRepositoryError>;
}
Loading

0 comments on commit 2e77175

Please sign in to comment.