From 96980becac951390fb4b4c7b1d1d0fae908ec2e5 Mon Sep 17 00:00:00 2001 From: Thomas Gauges Date: Mon, 4 Mar 2024 21:57:53 +0100 Subject: [PATCH] refactor!: rename "recipe file" to "recipe step file" --- ARCHITECTURE.md | 15 ++- CHANGELOG.md | 2 +- docs/architecture/database/schema.puml | 4 +- src-tauri/src/command/entity.rs | 2 +- src-tauri/src/command/entity/recipe_file.rs | 56 ----------- .../src/command/entity/recipe_step_file.rs | 62 ++++++++++++ src-tauri/src/command/external_recipe.rs | 6 +- src-tauri/src/command/ocr.rs | 7 +- src-tauri/src/entity.rs | 2 +- src-tauri/src/entity/recipe_step.rs | 19 ++-- .../{recipe_file.rs => recipe_step_file.rs} | 12 +-- src-tauri/src/entity_crud.rs | 2 +- .../{recipe_file.rs => recipe_step_file.rs} | 60 ++++++------ src-tauri/src/event/channel.rs | 6 +- src-tauri/src/main.rs | 23 ++--- .../src/migrator/m20230306_214922_1_0_0.rs | 12 ++- .../{recipe_file.rs => recipe_step_file.rs} | 53 +++++----- src-tauri/src/recipe_file_storage.rs | 96 ------------------- src-tauri/src/recipe_step_file_storage.rs | 96 +++++++++++++++++++ .../error.rs | 4 +- src-tauri/tauri.conf.json | 2 +- .../edit/RecipeStepFileEdit.svelte} | 8 +- .../list/RecipeStepFileList.svelte} | 21 ++-- .../view/RecipeStepFileView.svelte} | 26 ++--- .../recipe-step/view/RecipeStepView.svelte | 4 +- src/services/command/command-answer.ts | 14 +-- src/services/command/command-parameter.ts | 38 ++++---- src/services/command/command.ts | 12 +-- src/services/command/entity.ts | 64 +++++++------ src/services/event/event-channel.ts | 6 +- src/services/event/event-payload.ts | 6 +- .../repository/recipe-file-repository.ts | 52 ---------- .../repository/recipe-step-file-repository.ts | 58 +++++++++++ src/services/translation/en.ts | 2 +- src/services/translation/translations.ts | 2 +- ...rface.ts => recipe-step-file-interface.ts} | 10 +- src/types/filter/recipe-file-filter.ts | 12 --- src/types/filter/recipe-step-file-filter.ts | 12 +++ 38 files changed, 460 insertions(+), 428 deletions(-) delete mode 100644 src-tauri/src/command/entity/recipe_file.rs create mode 100644 src-tauri/src/command/entity/recipe_step_file.rs rename src-tauri/src/entity/{recipe_file.rs => recipe_step_file.rs} (75%) rename src-tauri/src/entity_crud/{recipe_file.rs => recipe_step_file.rs} (71%) rename src-tauri/src/migrator/m20230306_214922_1_0_0/{recipe_file.rs => recipe_step_file.rs} (56%) delete mode 100644 src-tauri/src/recipe_file_storage.rs create mode 100644 src-tauri/src/recipe_step_file_storage.rs rename src-tauri/src/{recipe_file_storage => recipe_step_file_storage}/error.rs (74%) rename src/components/content/entity/{recipe-file/edit/RecipeFileEdit.svelte => recipe-step-file/edit/RecipeStepFileEdit.svelte} (63%) rename src/components/content/entity/{recipe-file/list/RecipeFileList.svelte => recipe-step-file/list/RecipeStepFileList.svelte} (66%) rename src/components/content/entity/{recipe-file/view/RecipeFileView.svelte => recipe-step-file/view/RecipeStepFileView.svelte} (67%) delete mode 100644 src/services/store/repository/recipe-file-repository.ts create mode 100644 src/services/store/repository/recipe-step-file-repository.ts rename src/types/entity/{recipe-file-interface.ts => recipe-step-file-interface.ts} (62%) delete mode 100644 src/types/filter/recipe-file-filter.ts create mode 100644 src/types/filter/recipe-step-file-filter.ts diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index defd1ef..0394fa8 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -9,9 +9,11 @@ The source code for the backend lives in [src-tauri](./src-tauri) and is written ### Recipe Storage -Recipes need to be stored locally. The solution chosen for this project is an embedded relational database. This is supported by simple file storage for binary files like images. +Recipes need to be stored locally. The solution chosen for this project is an embedded relational database. This is +supported by simple file storage for binary files like images. -The database needs to implement the data schema outlined in the [PlantUML](https://plantuml.com/) file [schema.puml](docs/architecture/database/schema.puml). +The database needs to implement the data schema outlined in the [PlantUML](https://plantuml.com/) +file [schema.puml](docs/architecture/database/schema.puml). #### Database @@ -27,15 +29,18 @@ The connection to the database is handled via the object-relational manager [Sea ###### Migrations -SeaORM provides database migration functionality. This is implemented in the [migrator module](./src-tauri/src/migrator.rs). +SeaORM provides database migration functionality. This is implemented in +the [migrator module](./src-tauri/src/migrator.rs). #### File Storage -Binary files are not stored in the database but separately. Recipe file storage is implemented in the [recipe_file_storage module](./src-tauri/src/recipe_file_storage.rs). +Binary files are not stored in the database but separately. Recipe step file storage is implemented in +the [recipe_step_file_storage module](./src-tauri/src/recipe_step_file_storage.rs). ### OCR -Optical character recognition is integrated with [Tesseract](https://github.com/tesseract-ocr/tesseract) via the [tesseract-rs](https://github.com/antimatter15/tesseract-rs) crate. +Optical character recognition is integrated with [Tesseract](https://github.com/tesseract-ocr/tesseract) via +the [tesseract-rs](https://github.com/antimatter15/tesseract-rs) crate. ## Frontend diff --git a/CHANGELOG.md b/CHANGELOG.md index 1251899..c530807 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ Types of changes: ### Added - Implement recipe editing -- Implement recipe files (image/video/audio) with optical character recognition +- Implement recipe step files (image/video/audio) with optical character recognition - Add field "quality" to recipe step ingredients - Implement getting external recipes - Implement unit conversion diff --git a/docs/architecture/database/schema.puml b/docs/architecture/database/schema.puml index 2a86467..8e1cf63 100644 --- a/docs/architecture/database/schema.puml +++ b/docs/architecture/database/schema.puml @@ -49,7 +49,7 @@ entity "Ingredient" as ingredient { } ingredient "ingredient_id" ||--o{ recipe_step_ingredient -entity "Recipe File" as recipe_file { +entity "Recipe Step File" as recipe_step_file { id: INTEGER -- name: TEXT @@ -57,7 +57,7 @@ entity "Recipe File" as recipe_file { mime: TEXT uri: TEXT } -recipe_file }o--|| "recipe_step_id" recipe_step +recipe_step_file }o--|| "recipe_step_id" recipe_step entity "Unit Name" as unit_name { name: TEXT diff --git a/src-tauri/src/command/entity.rs b/src-tauri/src/command/entity.rs index 748a3d5..5a055ce 100644 --- a/src-tauri/src/command/entity.rs +++ b/src-tauri/src/command/entity.rs @@ -1,8 +1,8 @@ pub mod ingredient; pub mod recipe; -pub mod recipe_file; pub mod recipe_ingredient_draft; pub mod recipe_step; +pub mod recipe_step_file; pub mod recipe_step_ingredient; pub mod recipe_step_ingredient_draft; pub mod unit_name; diff --git a/src-tauri/src/command/entity/recipe_file.rs b/src-tauri/src/command/entity/recipe_file.rs deleted file mode 100644 index 5566b8d..0000000 --- a/src-tauri/src/command/entity/recipe_file.rs +++ /dev/null @@ -1,56 +0,0 @@ -use tauri::Manager; - -use crate::{ - command::error::{CommandError, CommandError::NotFound}, - entity::recipe_file::Model, - entity_crud::{ - recipe_file::{ - RecipeFileCondition, RecipeFileCreate, RecipeFileCrud, RecipeFileFilter, - RecipeFileUpdate, - }, - EntityCrudTrait, - }, -}; - -#[tauri::command] -pub async fn entity_create_recipe_file(create: RecipeFileCreate) -> Result { - let id = RecipeFileCrud::create(create).await?; - Ok(id) -} - -#[tauri::command] -pub async fn entity_read_recipe_file( - id: i64, - window: tauri::Window, -) -> Result { - let model_option = RecipeFileCrud::read(id).await?; - let model = model_option.ok_or(NotFound)?; - window.asset_protocol_scope().allow_file(&model.path)?; - Ok(model) -} - -#[tauri::command] -pub async fn entity_update_recipe_file(update: RecipeFileUpdate) -> Result<(), CommandError> { - RecipeFileCrud::update(update).await?; - Ok(()) -} - -#[tauri::command] -pub async fn entity_delete_recipe_file(id: i64) -> Result<(), CommandError> { - RecipeFileCrud::delete(id).await?; - Ok(()) -} - -#[tauri::command] -pub async fn entity_list_recipe_file(filter: RecipeFileFilter) -> Result, CommandError> { - let list = RecipeFileCrud::list(filter).await?; - Ok(list) -} - -#[tauri::command] -pub async fn entity_count_recipe_file( - condition: Option, -) -> Result { - let count = RecipeFileCrud::count(condition).await?; - Ok(count) -} diff --git a/src-tauri/src/command/entity/recipe_step_file.rs b/src-tauri/src/command/entity/recipe_step_file.rs new file mode 100644 index 0000000..0dbae59 --- /dev/null +++ b/src-tauri/src/command/entity/recipe_step_file.rs @@ -0,0 +1,62 @@ +use tauri::Manager; + +use crate::{ + command::error::{CommandError, CommandError::NotFound}, + entity::recipe_step_file::Model, + entity_crud::{ + recipe_step_file::{ + RecipeStepFileCondition, RecipeStepFileCreate, RecipeStepFileCrud, + RecipeStepFileFilter, RecipeStepFileUpdate, + }, + EntityCrudTrait, + }, +}; + +#[tauri::command] +pub async fn entity_create_recipe_step_file( + create: RecipeStepFileCreate, +) -> Result { + let id = RecipeStepFileCrud::create(create).await?; + Ok(id) +} + +#[tauri::command] +pub async fn entity_read_recipe_step_file( + id: i64, + window: tauri::Window, +) -> Result { + let model_option = RecipeStepFileCrud::read(id).await?; + let model = model_option.ok_or(NotFound)?; + window.asset_protocol_scope().allow_file(&model.path)?; + Ok(model) +} + +#[tauri::command] +pub async fn entity_update_recipe_step_file( + update: RecipeStepFileUpdate, +) -> Result<(), CommandError> { + RecipeStepFileCrud::update(update).await?; + Ok(()) +} + +#[tauri::command] +pub async fn entity_delete_recipe_step_file(id: i64) -> Result<(), CommandError> { + RecipeStepFileCrud::delete(id).await?; + Ok(()) +} + +#[tauri::command] +pub async fn entity_list_recipe_step_file( + filter: RecipeStepFileFilter, +) -> Result, CommandError> { + let list = RecipeStepFileCrud::list(filter).await?; + Ok(list) +} + +#[tauri::command] +pub async fn entity_count_recipe_step_file( + condition: Option, +) -> Result { + let count = RecipeStepFileCrud::count(condition).await?; + Ok(count) +} diff --git a/src-tauri/src/command/external_recipe.rs b/src-tauri/src/command/external_recipe.rs index f6a1e1a..5430aa5 100644 --- a/src-tauri/src/command/external_recipe.rs +++ b/src-tauri/src/command/external_recipe.rs @@ -2,9 +2,9 @@ use crate::{ command::error::CommandError, entity_crud::{ recipe::{RecipeCreate, RecipeCrud}, - recipe_file::{RecipeFileCreate, RecipeFileCreateUri, RecipeFileCrud}, recipe_ingredient_draft::{RecipeIngredientDraftCreate, RecipeIngredientDraftCrud}, recipe_step::{RecipeStepCreate, RecipeStepCrud}, + recipe_step_file::{RecipeStepFileCreate, RecipeStepFileCreateUri, RecipeStepFileCrud}, recipe_step_ingredient_draft::{ RecipeStepIngredientDraftCreate, RecipeStepIngredientDraftCrud, }, @@ -52,10 +52,10 @@ pub async fn external_recipe(url: String) -> Result { } for (i, file) in step.files.into_iter().enumerate() { tokio::spawn(async move { - RecipeFileCrud::create(RecipeFileCreate { + RecipeStepFileCrud::create(RecipeStepFileCreate { name: file.clone(), order: (i + 1) as i64, - uri: RecipeFileCreateUri::Url(file), + uri: RecipeStepFileCreateUri::Url(file), recipe_step_id, }) .await diff --git a/src-tauri/src/command/ocr.rs b/src-tauri/src/command/ocr.rs index 7afa04d..17b3839 100644 --- a/src-tauri/src/command/ocr.rs +++ b/src-tauri/src/command/ocr.rs @@ -7,10 +7,11 @@ use crate::{ entity_crud::EntityCrudTrait, }; -/// Get the optically recognized characters from the specified recipe file. +/// Get the optically recognized characters from the specified recipe step file. #[tauri::command] -pub async fn ocr(recipe_file_id: i64) -> Result { - let model_option = entity_crud::recipe_file::RecipeFileCrud::read(recipe_file_id).await?; +pub async fn ocr(recipe_step_file_id: i64) -> Result { + let model_option = + entity_crud::recipe_step_file::RecipeStepFileCrud::read(recipe_step_file_id).await?; let Some(model) = model_option else { return Err(CommandError::NotFound); }; diff --git a/src-tauri/src/entity.rs b/src-tauri/src/entity.rs index c78ef27..30d10f3 100644 --- a/src-tauri/src/entity.rs +++ b/src-tauri/src/entity.rs @@ -2,9 +2,9 @@ pub mod ingredient; pub mod recipe; -pub mod recipe_file; pub mod recipe_ingredient_draft; pub mod recipe_step; +pub mod recipe_step_file; pub mod recipe_step_ingredient; pub mod recipe_step_ingredient_draft; pub mod unit_name; diff --git a/src-tauri/src/entity/recipe_step.rs b/src-tauri/src/entity/recipe_step.rs index 4c4064a..2fa26c8 100644 --- a/src-tauri/src/entity/recipe_step.rs +++ b/src-tauri/src/entity/recipe_step.rs @@ -32,8 +32,8 @@ pub enum Relation { Recipe, #[sea_orm(has_many = "super::recipe_step_ingredient::Entity")] RecipeStepIngredient, - #[sea_orm(has_many = "super::recipe_file::Entity")] - RecipeFile, + #[sea_orm(has_many = "super::recipe_step_file::Entity")] + RecipeStepFile, } impl Related for Entity { @@ -48,9 +48,9 @@ impl Related for Entity { } } -impl Related for Entity { +impl Related for Entity { fn to() -> RelationDef { - Relation::RecipeFile.def() + Relation::RecipeStepFile.def() } } @@ -61,12 +61,15 @@ impl ActiveModelBehavior for ActiveModel { C: ConnectionTrait, { let model = self.clone().try_into_model()?; - let recipe_files = model - .find_related(super::recipe_file::Entity) + let recipe_step_files = model + .find_related(super::recipe_step_file::Entity) .all(db) .await?; - for recipe_file in recipe_files { - recipe_file.into_active_model().before_delete(db).await?; + for recipe_step_file in recipe_step_files { + recipe_step_file + .into_active_model() + .before_delete(db) + .await?; } Ok(self) } diff --git a/src-tauri/src/entity/recipe_file.rs b/src-tauri/src/entity/recipe_step_file.rs similarity index 75% rename from src-tauri/src/entity/recipe_file.rs rename to src-tauri/src/entity/recipe_step_file.rs index f9d8e24..65e071c 100644 --- a/src-tauri/src/entity/recipe_file.rs +++ b/src-tauri/src/entity/recipe_step_file.rs @@ -1,4 +1,4 @@ -//! This module implements the recipe file entity. +//! This module implements the recipe step file entity. //! //! See [`Model`] for more information. @@ -7,12 +7,12 @@ use log; use sea_orm::{entity::prelude::*, TryIntoModel}; use serde::Serialize; -/// This struct represents a recipe file. +/// This struct represents a recipe step file. /// -/// A recipe file is a supplementary binary file to a recipe. +/// A recipe step file is a supplementary binary file to a recipe step. #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize)] #[serde(rename_all = "camelCase")] -#[sea_orm(table_name = "recipe_file")] +#[sea_orm(table_name = "recipe_step_file")] pub struct Model { #[sea_orm(primary_key)] pub id: i64, @@ -48,9 +48,9 @@ impl ActiveModelBehavior for ActiveModel { C: ConnectionTrait, { let model = self.clone().try_into_model()?; - if let Err(err) = crate::recipe_file_storage::delete(&model).await { + if let Err(err) = crate::recipe_step_file_storage::delete(&model).await { log::warn!( - "Could not delete recipe file from storage while deleting entity: {}", + "Could not delete recipe step file from storage while deleting entity: {}", err ); }; diff --git a/src-tauri/src/entity_crud.rs b/src-tauri/src/entity_crud.rs index 294bb13..7b396bf 100644 --- a/src-tauri/src/entity_crud.rs +++ b/src-tauri/src/entity_crud.rs @@ -14,9 +14,9 @@ use crate::{database, window::get_window}; pub mod ingredient; pub mod recipe; -pub mod recipe_file; pub mod recipe_ingredient_draft; pub mod recipe_step; +pub mod recipe_step_file; pub mod recipe_step_ingredient; pub mod recipe_step_ingredient_draft; pub mod unit_name; diff --git a/src-tauri/src/entity_crud/recipe_file.rs b/src-tauri/src/entity_crud/recipe_step_file.rs similarity index 71% rename from src-tauri/src/entity_crud/recipe_file.rs rename to src-tauri/src/entity_crud/recipe_step_file.rs index 455e735..ace1ae5 100644 --- a/src-tauri/src/entity_crud/recipe_file.rs +++ b/src-tauri/src/entity_crud/recipe_step_file.rs @@ -1,4 +1,4 @@ -//! This module implements [`EntityCrudTrait`] for [`crate::entity::recipe_file`]. +//! This module implements [`EntityCrudTrait`] for [`crate::entity::recipe_step_file`]. use std::{fs, str::FromStr}; @@ -17,43 +17,43 @@ use tempfile::NamedTempFile; use url::Url; use crate::{ - entity::recipe_file::{ActiveModel, Column, Entity, Model, PrimaryKey, Relation}, + entity::recipe_step_file::{ActiveModel, Column, Entity, Model, PrimaryKey, Relation}, entity_crud::{EntityCrudTrait, Filter, Order, OrderBy, TryIntoActiveModel}, event::channel::{ - ENTITY_ACTION_CREATED_RECIPE_FILE, ENTITY_ACTION_DELETED_RECIPE_FILE, - ENTITY_ACTION_UPDATED_RECIPE_FILE, + ENTITY_ACTION_CREATED_RECIPE_STEP_FILE, ENTITY_ACTION_DELETED_RECIPE_STEP_FILE, + ENTITY_ACTION_UPDATED_RECIPE_STEP_FILE, }, - recipe_file_storage, + recipe_step_file_storage, }; #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] -pub enum RecipeFileCreateUri { +pub enum RecipeStepFileCreateUri { Path(String), Url(String), } #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct RecipeFileCreate { +pub struct RecipeStepFileCreate { pub name: String, pub order: i64, - pub uri: RecipeFileCreateUri, + pub uri: RecipeStepFileCreateUri, pub recipe_step_id: i64, } #[async_trait] -impl TryIntoActiveModel for RecipeFileCreate { - /// Transform [`RecipeFileCreate`] into an [`ActiveModel`] by guessing the mime of and maybe downloading the file. +impl TryIntoActiveModel for RecipeStepFileCreate { + /// Transform [`RecipeStepFileCreate`] into an [`ActiveModel`] by guessing the mime of and maybe downloading the file. async fn try_into_active_model(self) -> Result { let (mime, path) = match self.uri { - RecipeFileCreateUri::Path(path) => { + RecipeStepFileCreateUri::Path(path) => { let mime = mime_guess::from_path(&path) .first_or(mime::APPLICATION_OCTET_STREAM) .to_string(); (Set(mime), Set(path)) } - RecipeFileCreateUri::Url(url) => { + RecipeStepFileCreateUri::Url(url) => { let url = Url::from_str(&url)?; let response = reqwest::get(url).await?; let mime = response @@ -83,13 +83,13 @@ impl TryIntoActiveModel for RecipeFileCreate { #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct RecipeFileUpdate { +pub struct RecipeStepFileUpdate { pub id: i64, pub name: Option, pub order: Option, } -impl IntoActiveModel for RecipeFileUpdate { +impl IntoActiveModel for RecipeStepFileUpdate { fn into_active_model(self) -> ActiveModel { ActiveModel { id: Unchanged(self.id), @@ -108,15 +108,15 @@ impl IntoActiveModel for RecipeFileUpdate { } } -pub type RecipeFileFilter = Filter; +pub type RecipeStepFileFilter = Filter; #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct RecipeFileCondition { +pub struct RecipeStepFileCondition { pub recipe_step_id: Option, } -impl IntoCondition for RecipeFileCondition { +impl IntoCondition for RecipeStepFileCondition { fn into_condition(self) -> Condition { Condition::all().add_option( self.recipe_step_id @@ -127,37 +127,37 @@ impl IntoCondition for RecipeFileCondition { #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] -pub enum RecipeFileOrderBy { +pub enum RecipeStepFileOrderBy { Order(Order), } -impl OrderBy for RecipeFileOrderBy { +impl OrderBy for RecipeStepFileOrderBy { type Entity = Entity; fn add(self, select: Select) -> Select { match self { - RecipeFileOrderBy::Order(order) => select.order_by(Column::Order, order.into()), + RecipeStepFileOrderBy::Order(order) => select.order_by(Column::Order, order.into()), } } } -pub struct RecipeFileCrud {} +pub struct RecipeStepFileCrud {} #[async_trait] -impl EntityCrudTrait for RecipeFileCrud { +impl EntityCrudTrait for RecipeStepFileCrud { type Entity = Entity; type Model = Model; type ActiveModel = ActiveModel; type Column = Column; type Relation = Relation; type PrimaryKey = PrimaryKey; - type EntityCreate = RecipeFileCreate; - type EntityUpdate = RecipeFileUpdate; - type EntityCondition = RecipeFileCondition; - type EntityOrderBy = RecipeFileOrderBy; + type EntityCreate = RecipeStepFileCreate; + type EntityUpdate = RecipeStepFileUpdate; + type EntityCondition = RecipeStepFileCondition; + type EntityOrderBy = RecipeStepFileOrderBy; async fn post_create(model: Model, txn: &DatabaseTransaction) -> Result { - let path = recipe_file_storage::create(&model).await?; + let path = recipe_step_file_storage::create(&model).await?; let mut active_model = model.into_active_model(); active_model.path = Set(path.to_string_lossy().to_string()); let model = active_model.update(txn).await?; @@ -173,14 +173,14 @@ impl EntityCrudTrait for RecipeFileCrud { } fn entity_action_created_channel() -> &'static str { - ENTITY_ACTION_CREATED_RECIPE_FILE + ENTITY_ACTION_CREATED_RECIPE_STEP_FILE } fn entity_action_updated_channel() -> &'static str { - ENTITY_ACTION_UPDATED_RECIPE_FILE + ENTITY_ACTION_UPDATED_RECIPE_STEP_FILE } fn entity_action_deleted_channel() -> &'static str { - ENTITY_ACTION_DELETED_RECIPE_FILE + ENTITY_ACTION_DELETED_RECIPE_STEP_FILE } } diff --git a/src-tauri/src/event/channel.rs b/src-tauri/src/event/channel.rs index ab6293a..35595ef 100644 --- a/src-tauri/src/event/channel.rs +++ b/src-tauri/src/event/channel.rs @@ -15,9 +15,9 @@ pub const ENTITY_ACTION_UPDATED_RECIPE_INGREDIENT_DRAFT: &str = pub const ENTITY_ACTION_DELETED_RECIPE_INGREDIENT_DRAFT: &str = "ENTITY_ACTION_DELETED_RECIPE_INGREDIENT_DRAFT"; -pub const ENTITY_ACTION_CREATED_RECIPE_FILE: &str = "ENTITY_ACTION_CREATED_RECIPE_FILE"; -pub const ENTITY_ACTION_UPDATED_RECIPE_FILE: &str = "ENTITY_ACTION_UPDATED_RECIPE_FILE"; -pub const ENTITY_ACTION_DELETED_RECIPE_FILE: &str = "ENTITY_ACTION_DELETED_RECIPE_FILE"; +pub const ENTITY_ACTION_CREATED_RECIPE_STEP_FILE: &str = "ENTITY_ACTION_CREATED_RECIPE_STEP_FILE"; +pub const ENTITY_ACTION_UPDATED_RECIPE_STEP_FILE: &str = "ENTITY_ACTION_UPDATED_RECIPE_STEP_FILE"; +pub const ENTITY_ACTION_DELETED_RECIPE_STEP_FILE: &str = "ENTITY_ACTION_DELETED_RECIPE_STEP_FILE"; pub const ENTITY_ACTION_CREATED_RECIPE_STEP_INGREDIENT: &str = "ENTITY_ACTION_CREATED_RECIPE_STEP_INGREDIENT"; diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 189ad52..d4341f1 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -15,10 +15,6 @@ use crate::command::{ entity_count_recipe, entity_create_recipe, entity_delete_recipe, entity_list_recipe, entity_read_recipe, entity_update_recipe, }, - recipe_file::{ - entity_count_recipe_file, entity_create_recipe_file, entity_delete_recipe_file, - entity_list_recipe_file, entity_read_recipe_file, entity_update_recipe_file, - }, recipe_ingredient_draft::{ entity_count_recipe_ingredient_draft, entity_create_recipe_ingredient_draft, entity_delete_recipe_ingredient_draft, entity_list_recipe_ingredient_draft, @@ -28,6 +24,11 @@ use crate::command::{ entity_count_recipe_step, entity_create_recipe_step, entity_delete_recipe_step, entity_list_recipe_step, entity_read_recipe_step, entity_update_recipe_step, }, + recipe_step_file::{ + entity_count_recipe_step_file, entity_create_recipe_step_file, + entity_delete_recipe_step_file, entity_list_recipe_step_file, + entity_read_recipe_step_file, entity_update_recipe_step_file, + }, recipe_step_ingredient::{ entity_count_recipe_step_ingredient, entity_create_recipe_step_ingredient, entity_delete_recipe_step_ingredient, entity_list_recipe_step_ingredient, @@ -61,7 +62,7 @@ mod fs; mod log; mod migrator; mod path; -mod recipe_file_storage; +mod recipe_step_file_storage; mod scraper; mod unit_conversion; mod window; @@ -100,12 +101,12 @@ fn setup() -> tauri::Builder { entity_delete_recipe_ingredient_draft, entity_list_recipe_ingredient_draft, entity_count_recipe_ingredient_draft, - entity_create_recipe_file, - entity_read_recipe_file, - entity_update_recipe_file, - entity_delete_recipe_file, - entity_list_recipe_file, - entity_count_recipe_file, + entity_create_recipe_step_file, + entity_read_recipe_step_file, + entity_update_recipe_step_file, + entity_delete_recipe_step_file, + entity_list_recipe_step_file, + entity_count_recipe_step_file, entity_create_recipe_step_ingredient, entity_read_recipe_step_ingredient, entity_update_recipe_step_ingredient, diff --git a/src-tauri/src/migrator/m20230306_214922_1_0_0.rs b/src-tauri/src/migrator/m20230306_214922_1_0_0.rs index e7e8691..b7af43c 100644 --- a/src-tauri/src/migrator/m20230306_214922_1_0_0.rs +++ b/src-tauri/src/migrator/m20230306_214922_1_0_0.rs @@ -6,9 +6,9 @@ use sea_orm_migration::prelude::*; mod ingredient; mod recipe; -mod recipe_file; mod recipe_ingredient_draft; mod recipe_step; +mod recipe_step_file; mod recipe_step_ingredient; mod recipe_step_ingredient_draft; mod unit_name; @@ -23,7 +23,7 @@ impl MigrationTrait for Migration { recipe::up(manager).await?; recipe_ingredient_draft::up(manager).await?; recipe_step::up(manager).await?; - recipe_file::up(manager).await?; + recipe_step_file::up(manager).await?; recipe_step_ingredient::up(manager).await?; recipe_step_ingredient_draft::up(manager).await?; unit_name::up(manager).await?; @@ -35,11 +35,13 @@ impl MigrationTrait for Migration { mod tests { use ingredient::tests::{assert_ingredient_indices, assert_ingredient_schema}; use recipe::tests::{assert_recipe_indices, assert_recipe_schema}; - use recipe_file::tests::{assert_recipe_file_indices, assert_recipe_file_schema}; use recipe_ingredient_draft::tests::{ assert_recipe_ingredient_draft_indices, assert_recipe_ingredient_draft_schema, }; use recipe_step::tests::{assert_recipe_step_indices, assert_recipe_step_schema}; + use recipe_step_file::tests::{ + assert_recipe_step_file_indices, assert_recipe_step_file_schema, + }; use recipe_step_ingredient::tests::{ assert_recipe_step_ingredient_indices, assert_recipe_step_ingredient_schema, }; @@ -68,8 +70,8 @@ mod tests { assert_recipe_ingredient_draft_indices(&db).await; assert_recipe_step_schema(&db).await; assert_recipe_step_indices(&db).await; - assert_recipe_file_schema(&db).await; - assert_recipe_file_indices(&db).await; + assert_recipe_step_file_schema(&db).await; + assert_recipe_step_file_indices(&db).await; assert_recipe_step_ingredient_schema(&db).await; assert_recipe_step_ingredient_indices(&db).await; assert_recipe_step_ingredient_draft_schema(&db).await; diff --git a/src-tauri/src/migrator/m20230306_214922_1_0_0/recipe_file.rs b/src-tauri/src/migrator/m20230306_214922_1_0_0/recipe_step_file.rs similarity index 56% rename from src-tauri/src/migrator/m20230306_214922_1_0_0/recipe_file.rs rename to src-tauri/src/migrator/m20230306_214922_1_0_0/recipe_step_file.rs index 5bfa59e..6996e7f 100644 --- a/src-tauri/src/migrator/m20230306_214922_1_0_0/recipe_file.rs +++ b/src-tauri/src/migrator/m20230306_214922_1_0_0/recipe_step_file.rs @@ -1,4 +1,4 @@ -//! This module implements the creation of [`crate::entity::recipe_file`]. +//! This module implements the creation of [`crate::entity::recipe_step_file`]. use sea_orm_migration::prelude::*; @@ -8,33 +8,33 @@ pub async fn up(manager: &SchemaManager<'_>) -> Result<(), DbErr> { manager .create_table( Table::create() - .table(RecipeFile::Table) + .table(RecipeStepFile::Table) .col( - ColumnDef::new(RecipeFile::Id) + ColumnDef::new(RecipeStepFile::Id) .integer() .not_null() .auto_increment() .primary_key(), ) - .col(ColumnDef::new(RecipeFile::Name).string().not_null()) - .col(ColumnDef::new(RecipeFile::Order).integer().not_null()) - .col(ColumnDef::new(RecipeFile::Mime).string().not_null()) - .col(ColumnDef::new(RecipeFile::Path).string().not_null()) + .col(ColumnDef::new(RecipeStepFile::Name).string().not_null()) + .col(ColumnDef::new(RecipeStepFile::Order).integer().not_null()) + .col(ColumnDef::new(RecipeStepFile::Mime).string().not_null()) + .col(ColumnDef::new(RecipeStepFile::Path).string().not_null()) .col( - ColumnDef::new(RecipeFile::RecipeStepId) + ColumnDef::new(RecipeStepFile::RecipeStepId) .integer() .not_null(), ) .foreign_key( ForeignKey::create() - .from(RecipeFile::Table, RecipeFile::RecipeStepId) + .from(RecipeStepFile::Table, RecipeStepFile::RecipeStepId) .to(RecipeStep::Table, RecipeStep::Id) .on_delete(ForeignKeyAction::Cascade), ) .index( Index::create() - .col(RecipeFile::Order) - .col(RecipeFile::RecipeStepId) + .col(RecipeStepFile::Order) + .col(RecipeStepFile::RecipeStepId) .unique(), ) .to_owned(), @@ -43,18 +43,21 @@ pub async fn up(manager: &SchemaManager<'_>) -> Result<(), DbErr> { manager .create_index( Index::create() - .name(&index_name(&RecipeFile::Table, &RecipeFile::Order)) - .table(RecipeFile::Table) - .col(RecipeFile::Order) + .name(&index_name(&RecipeStepFile::Table, &RecipeStepFile::Order)) + .table(RecipeStepFile::Table) + .col(RecipeStepFile::Order) .to_owned(), ) .await?; manager .create_index( Index::create() - .name(&index_name(&RecipeFile::Table, &RecipeFile::RecipeStepId)) - .table(RecipeFile::Table) - .col(RecipeFile::RecipeStepId) + .name(&index_name( + &RecipeStepFile::Table, + &RecipeStepFile::RecipeStepId, + )) + .table(RecipeStepFile::Table) + .col(RecipeStepFile::RecipeStepId) .to_owned(), ) .await?; @@ -62,7 +65,7 @@ pub async fn up(manager: &SchemaManager<'_>) -> Result<(), DbErr> { } #[derive(Iden)] -enum RecipeFile { +enum RecipeStepFile { Table, Id, Name, @@ -79,11 +82,11 @@ pub mod tests { use crate::database::tests::{get_table_indices, get_table_schema}; - pub async fn assert_recipe_file_schema(db: &DatabaseConnection) { - let table_schema = get_table_schema("recipe_file", db).await; + pub async fn assert_recipe_step_file_schema(db: &DatabaseConnection) { + let table_schema = get_table_schema("recipe_step_file", db).await; assert_str_eq!( table_schema, - "CREATE TABLE \"recipe_file\" ( \ + "CREATE TABLE \"recipe_step_file\" ( \ \"id\" integer NOT NULL PRIMARY KEY AUTOINCREMENT, \ \"name\" text NOT NULL, \ \"order\" integer NOT NULL, \ @@ -96,16 +99,16 @@ pub mod tests { ); } - pub async fn assert_recipe_file_indices(db: &DatabaseConnection) { - let indices = get_table_indices("recipe_file", db).await; + pub async fn assert_recipe_step_file_indices(db: &DatabaseConnection) { + let indices = get_table_indices("recipe_step_file", db).await; assert_eq!( indices, vec![ String::from( - "CREATE INDEX \"idx-recipe_file-order\" ON \"recipe_file\" (\"order\")" + "CREATE INDEX \"idx-recipe_step_file-order\" ON \"recipe_step_file\" (\"order\")" ), String::from( - "CREATE INDEX \"idx-recipe_file-recipe_step_id\" ON \"recipe_file\" (\"recipe_step_id\")" + "CREATE INDEX \"idx-recipe_step_file-recipe_step_id\" ON \"recipe_step_file\" (\"recipe_step_id\")" ), ] ) diff --git a/src-tauri/src/recipe_file_storage.rs b/src-tauri/src/recipe_file_storage.rs deleted file mode 100644 index 93eb23e..0000000 --- a/src-tauri/src/recipe_file_storage.rs +++ /dev/null @@ -1,96 +0,0 @@ -//! This module handles storage of binary files like images. - -use std::path::PathBuf; - -use sea_orm::{DbErr, EntityTrait}; -use tokio::fs; - -use crate::{ - database, - entity::{recipe_file::Model, recipe_step}, - path::app_data_dir, - recipe_file_storage::error::RecipeFileStorageError, -}; - -pub mod error; - -/// Creates a new file by copying from the path specified in the existing recipe files entity. -/// -/// It returns the new path. -/// -/// # Errors -/// -/// - [`RecipeFileStorageError::Io`] when there is an I/O error while writing the file -/// - [`RecipeFileStorageError::Db`] when [`file`] errors -pub async fn create(recipe_file: &Model) -> Result { - let target = get_file(recipe_file).await?; - let source = PathBuf::from(&recipe_file.path); - fs::create_dir_all(&target.parent().unwrap()).await?; - fs::copy(&source, &target).await?; - Ok(target) -} - -/// Deletes the stored recipe file associated with an recipe file entity. -/// -/// Also delete all empty parent directories up to and including [`get_dir`]. -/// -/// # Errors -/// -/// - [`RecipeFileStorageError::Io`] when there is an I/O error while deleting the file -/// - [`RecipeFileStorageError::Db`] when [`file`] errors -pub async fn delete(recipe_file: &Model) -> Result<(), RecipeFileStorageError> { - let file = PathBuf::from(&recipe_file.path); - fs::remove_file(file.clone()).await?; - let mut dir_option = file.parent(); - while let Some(dir) = dir_option { - if !dir.starts_with(get_dir()) || fs::remove_dir(dir).await.is_err() { - break; - } - dir_option = dir.parent(); - } - Ok(()) -} - -/// Get the recipe file storage root directory. -fn get_dir() -> PathBuf { - let mut dir = app_data_dir(); - dir.push("recipe_files"); - dir -} - -/// Get the path segments of the canonical file path relative to the recipe file storage root. -/// -/// See [`get_dir`] for getting the recipe file storage root directory. -/// -/// # Errors -/// -/// - when the recipe step corresponding to the recipe file can not be found in the database -async fn path_segments(recipe_file: &Model) -> Result, DbErr> { - let db = database::connect().await; - let recipe_step = recipe_step::Entity::find_by_id(recipe_file.recipe_step_id) - .one(db) - .await? - .unwrap(); - let file_name = match mime2ext::mime2ext(&recipe_file.mime) { - Some(extension) => format!("{}.{extension}", recipe_file.id), - None => recipe_file.id.to_string(), - }; - Ok(vec![ - recipe_step.recipe_id.to_string(), - recipe_step.id.to_string(), - file_name, - ]) -} - -/// Get the canonical file path of a recipe file entity. -/// -/// # Errors -/// -/// - when [`path_segments`] returns an error variant -async fn get_file(recipe_file: &Model) -> Result { - let mut file = get_dir(); - for path_segment in path_segments(recipe_file).await? { - file.push(path_segment); - } - Ok(file) -} diff --git a/src-tauri/src/recipe_step_file_storage.rs b/src-tauri/src/recipe_step_file_storage.rs new file mode 100644 index 0000000..b8f4be9 --- /dev/null +++ b/src-tauri/src/recipe_step_file_storage.rs @@ -0,0 +1,96 @@ +//! This module handles storage of binary files like images. + +use std::path::PathBuf; + +use sea_orm::{DbErr, EntityTrait}; +use tokio::fs; + +use crate::{ + database, + entity::{recipe_step, recipe_step_file::Model}, + path::app_data_dir, + recipe_step_file_storage::error::RecipeStepFileStorageError, +}; + +pub mod error; + +/// Creates a new file by copying from the path specified in the existing recipe step files entity. +/// +/// It returns the new path. +/// +/// # Errors +/// +/// - [`RecipeStepFileStorageError::Io`] when there is an I/O error while writing the file +/// - [`RecipeStepFileStorageError::Db`] when [`file`] errors +pub async fn create(recipe_step_file: &Model) -> Result { + let target = get_file(recipe_step_file).await?; + let source = PathBuf::from(&recipe_step_file.path); + fs::create_dir_all(&target.parent().unwrap()).await?; + fs::copy(&source, &target).await?; + Ok(target) +} + +/// Deletes the stored recipe step file associated with a recipe step file entity. +/// +/// Also delete all empty parent directories up to and including [`get_dir`]. +/// +/// # Errors +/// +/// - [`RecipeStepFileStorageError::Io`] when there is an I/O error while deleting the file +/// - [`RecipeStepFileStorageError::Db`] when [`file`] errors +pub async fn delete(recipe_step_file: &Model) -> Result<(), RecipeStepFileStorageError> { + let file = PathBuf::from(&recipe_step_file.path); + fs::remove_file(file.clone()).await?; + let mut dir_option = file.parent(); + while let Some(dir) = dir_option { + if !dir.starts_with(get_dir()) || fs::remove_dir(dir).await.is_err() { + break; + } + dir_option = dir.parent(); + } + Ok(()) +} + +/// Get the recipe step file storage root directory. +fn get_dir() -> PathBuf { + let mut dir = app_data_dir(); + dir.push("recipe_step_files"); + dir +} + +/// Get the path segments of the canonical file path relative to the recipe step file storage root. +/// +/// See [`get_dir`] for getting the recipe step file storage root directory. +/// +/// # Errors +/// +/// - when the recipe step corresponding to the recipe step file can not be found in the database +async fn path_segments(recipe_step_file: &Model) -> Result, DbErr> { + let db = database::connect().await; + let recipe_step = recipe_step::Entity::find_by_id(recipe_step_file.recipe_step_id) + .one(db) + .await? + .unwrap(); + let file_name = match mime2ext::mime2ext(&recipe_step_file.mime) { + Some(extension) => format!("{}.{extension}", recipe_step_file.id), + None => recipe_step_file.id.to_string(), + }; + Ok(vec![ + recipe_step.recipe_id.to_string(), + recipe_step.id.to_string(), + file_name, + ]) +} + +/// Get the canonical file path of a recipe step file entity. +/// +/// # Errors +/// +/// - when [`path_segments`] returns an error variant +async fn get_file(recipe_step_file: &Model) -> Result { + let mut file = get_dir(); + for path_segment in path_segments(recipe_step_file).await? { + file.push(path_segment); + } + Ok(file) +} diff --git a/src-tauri/src/recipe_file_storage/error.rs b/src-tauri/src/recipe_step_file_storage/error.rs similarity index 74% rename from src-tauri/src/recipe_file_storage/error.rs rename to src-tauri/src/recipe_step_file_storage/error.rs index c42fa69..cb71a90 100644 --- a/src-tauri/src/recipe_file_storage/error.rs +++ b/src-tauri/src/recipe_step_file_storage/error.rs @@ -1,9 +1,9 @@ -//! This module contains the [`std::error::Error`] for the [`crate::recipe_file_storage`] module. +//! This module contains the [`std::error::Error`] for the [`crate::recipe_step_file_storage`] module. use thiserror::Error; #[derive(Debug, Error)] -pub enum RecipeFileStorageError { +pub enum RecipeStepFileStorageError { #[error(transparent)] Io(#[from] std::io::Error), #[error(transparent)] diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 4b5d226..38f88cc 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -53,7 +53,7 @@ } }, "security": { - "csp": "default-src 'self' recipe-file: https://recipe-file.localhost" + "csp": "default-src 'self'" }, "updater": { "active": false diff --git a/src/components/content/entity/recipe-file/edit/RecipeFileEdit.svelte b/src/components/content/entity/recipe-step-file/edit/RecipeStepFileEdit.svelte similarity index 63% rename from src/components/content/entity/recipe-file/edit/RecipeFileEdit.svelte rename to src/components/content/entity/recipe-step-file/edit/RecipeStepFileEdit.svelte index d940b2f..fb20ab8 100644 --- a/src/components/content/entity/recipe-file/edit/RecipeFileEdit.svelte +++ b/src/components/content/entity/recipe-step-file/edit/RecipeStepFileEdit.svelte @@ -1,6 +1,6 @@ diff --git a/src/components/content/entity/recipe-file/list/RecipeFileList.svelte b/src/components/content/entity/recipe-step-file/list/RecipeStepFileList.svelte similarity index 66% rename from src/components/content/entity/recipe-file/list/RecipeFileList.svelte rename to src/components/content/entity/recipe-step-file/list/RecipeStepFileList.svelte index a02f4e5..5e482c2 100644 --- a/src/components/content/entity/recipe-file/list/RecipeFileList.svelte +++ b/src/components/content/entity/recipe-step-file/list/RecipeStepFileList.svelte @@ -1,17 +1,17 @@ -{#if isLoaded($recipeFile)} +{#if isLoaded($recipeStepFile)} {#if mimeType === "image"} - {$recipeFile.name} + {$recipeStepFile.name} {:else if mimeType === "video"} {:else if mimeType === "audio"} @@ -55,7 +55,7 @@ It includes functionality to optically recognize characters in the recipe file. {messages.labels.actions.ocr.format()} diff --git a/src/components/content/entity/recipe-step/view/RecipeStepView.svelte b/src/components/content/entity/recipe-step/view/RecipeStepView.svelte index a6c6627..7634ba8 100644 --- a/src/components/content/entity/recipe-step/view/RecipeStepView.svelte +++ b/src/components/content/entity/recipe-step/view/RecipeStepView.svelte @@ -8,7 +8,7 @@ This component displays the content of a recipe step. import { messages } from "../../../../../services/translation/en.ts"; import { isLoaded } from "../../../../../services/util/loadable.ts"; import Editable from "../../../../layout/Editable.svelte"; - import RecipeFileList from "../../recipe-file/list/RecipeFileList.svelte"; + import RecipeStepFileList from "../../recipe-step-file/list/RecipeStepFileList.svelte"; import RecipeStepIngredientList from "../../recipe-step-ingredient/list/RecipeStepIngredientList.svelte"; import RecipeStepEdit from "../edit/RecipeStepEdit.svelte"; @@ -48,5 +48,5 @@ This component displays the content of a recipe step.

{messages.headings.files.format()}

- + {/if} diff --git a/src/services/command/command-answer.ts b/src/services/command/command-answer.ts index 075b744..7283a33 100644 --- a/src/services/command/command-answer.ts +++ b/src/services/command/command-answer.ts @@ -1,7 +1,7 @@ import type { IngredientInterface } from "../../types/entity/ingredient-interface.ts"; -import type { RecipeFileInterface } from "../../types/entity/recipe-file-interface.ts"; import type { RecipeIngredientDraftInterface } from "../../types/entity/recipe-ingredient-draft-interface.ts"; import type { RecipeInterface } from "../../types/entity/recipe-interface.ts"; +import type { RecipeStepFileInterface } from "../../types/entity/recipe-step-file-interface.ts"; import type { RecipeStepIngredientDraftInterface } from "../../types/entity/recipe-step-ingredient-draft-interface.ts"; import type { RecipeStepIngredientInterface } from "../../types/entity/recipe-step-ingredient-interface.ts"; import type { RecipeStepInterface } from "../../types/entity/recipe-step-interface.ts"; @@ -31,12 +31,12 @@ type CommandAnswerMap = { [Command.ENTITY_LIST_RECIPE_INGREDIENT_DRAFT]: number[]; [Command.ENTITY_COUNT_RECIPE_INGREDIENT_DRAFT]: number; - [Command.ENTITY_CREATE_RECIPE_FILE]: number; - [Command.ENTITY_READ_RECIPE_FILE]: RecipeFileInterface; - [Command.ENTITY_UPDATE_RECIPE_FILE]: void; - [Command.ENTITY_DELETE_RECIPE_FILE]: void; - [Command.ENTITY_LIST_RECIPE_FILE]: number[]; - [Command.ENTITY_COUNT_RECIPE_FILE]: number; + [Command.ENTITY_CREATE_RECIPE_STEP_FILE]: number; + [Command.ENTITY_READ_RECIPE_STEP_FILE]: RecipeStepFileInterface; + [Command.ENTITY_UPDATE_RECIPE_STEP_FILE]: void; + [Command.ENTITY_DELETE_RECIPE_STEP_FILE]: void; + [Command.ENTITY_LIST_RECIPE_STEP_FILE]: number[]; + [Command.ENTITY_COUNT_RECIPE_STEP_FILE]: number; [Command.ENTITY_CREATE_RECIPE_STEP_INGREDIENT]: number; [Command.ENTITY_READ_RECIPE_STEP_INGREDIENT]: RecipeStepIngredientInterface; diff --git a/src/services/command/command-parameter.ts b/src/services/command/command-parameter.ts index 6bb53c6..0095ae5 100644 --- a/src/services/command/command-parameter.ts +++ b/src/services/command/command-parameter.ts @@ -2,10 +2,6 @@ import type { IngredientCreateInterface, IngredientUpdateInterface, } from "../../types/entity/ingredient-interface.ts"; -import type { - RecipeFileCreateInterface, - RecipeFileUpdateInterface, -} from "../../types/entity/recipe-file-interface.ts"; import type { RecipeIngredientDraftCreateInterface, RecipeIngredientDraftUpdateInterface, @@ -14,6 +10,10 @@ import type { RecipeCreateInterface, RecipeUpdateInterface, } from "../../types/entity/recipe-interface.ts"; +import type { + RecipeStepFileCreateInterface, + RecipeStepFileUpdateInterface, +} from "../../types/entity/recipe-step-file-interface.ts"; import type { RecipeStepIngredientDraftCreateInterface, RecipeStepIngredientDraftUpdateInterface, @@ -35,10 +35,6 @@ import type { IngredientCondition, IngredientFilter, } from "../../types/filter/ingredient-filter.ts"; -import type { - RecipeFileCondition, - RecipeFileFilter, -} from "../../types/filter/recipe-file-filter.ts"; import type { RecipeCondition, RecipeFilter, @@ -47,6 +43,10 @@ import type { RecipeIngredientDraftCondition, RecipeIngredientDraftFilter, } from "../../types/filter/recipe-ingredient-draft-filter.ts"; +import type { + RecipeStepFileCondition, + RecipeStepFileFilter, +} from "../../types/filter/recipe-step-file-filter.ts"; import type { RecipeStepCondition, RecipeStepFilter, @@ -95,19 +95,19 @@ type CommandParameterMap = { condition?: RecipeIngredientDraftCondition; }; - [Command.ENTITY_CREATE_RECIPE_FILE]: { - create: RecipeFileCreateInterface; + [Command.ENTITY_CREATE_RECIPE_STEP_FILE]: { + create: RecipeStepFileCreateInterface; }; - [Command.ENTITY_READ_RECIPE_FILE]: { id: number }; - [Command.ENTITY_UPDATE_RECIPE_FILE]: { - update: RecipeFileUpdateInterface; + [Command.ENTITY_READ_RECIPE_STEP_FILE]: { id: number }; + [Command.ENTITY_UPDATE_RECIPE_STEP_FILE]: { + update: RecipeStepFileUpdateInterface; }; - [Command.ENTITY_DELETE_RECIPE_FILE]: { id: number }; - [Command.ENTITY_LIST_RECIPE_FILE]: { - filter: RecipeFileFilter; + [Command.ENTITY_DELETE_RECIPE_STEP_FILE]: { id: number }; + [Command.ENTITY_LIST_RECIPE_STEP_FILE]: { + filter: RecipeStepFileFilter; }; - [Command.ENTITY_COUNT_RECIPE_FILE]: { - condition?: RecipeFileCondition; + [Command.ENTITY_COUNT_RECIPE_STEP_FILE]: { + condition?: RecipeStepFileCondition; }; [Command.ENTITY_CREATE_RECIPE_STEP_INGREDIENT]: { @@ -156,7 +156,7 @@ type CommandParameterMap = { [Command.EXTERNAL_RECIPE]: { url: string }; - [Command.OCR]: { recipeFileId: number }; + [Command.OCR]: { recipeStepFileId: number }; [Command.UNIT_CONVERT]: { value: number; unit: Unit }; diff --git a/src/services/command/command.ts b/src/services/command/command.ts index 2d1ff27..13ef50e 100644 --- a/src/services/command/command.ts +++ b/src/services/command/command.ts @@ -20,12 +20,12 @@ export const enum Command { ENTITY_LIST_RECIPE_INGREDIENT_DRAFT = "entity_list_recipe_ingredient_draft", ENTITY_COUNT_RECIPE_INGREDIENT_DRAFT = "entity_count_recipe_ingredient_draft", - ENTITY_CREATE_RECIPE_FILE = "entity_create_recipe_file", - ENTITY_READ_RECIPE_FILE = "entity_read_recipe_file", - ENTITY_UPDATE_RECIPE_FILE = "entity_update_recipe_file", - ENTITY_DELETE_RECIPE_FILE = "entity_delete_recipe_file", - ENTITY_LIST_RECIPE_FILE = "entity_list_recipe_file", - ENTITY_COUNT_RECIPE_FILE = "entity_count_recipe_file", + ENTITY_CREATE_RECIPE_STEP_FILE = "entity_create_recipe_step_file", + ENTITY_READ_RECIPE_STEP_FILE = "entity_read_recipe_step_file", + ENTITY_UPDATE_RECIPE_STEP_FILE = "entity_update_recipe_step_file", + ENTITY_DELETE_RECIPE_STEP_FILE = "entity_delete_recipe_step_file", + ENTITY_LIST_RECIPE_STEP_FILE = "entity_list_recipe_step_file", + ENTITY_COUNT_RECIPE_STEP_FILE = "entity_count_recipe_step_file", ENTITY_CREATE_RECIPE_STEP_INGREDIENT = "entity_create_recipe_step_ingredient", ENTITY_READ_RECIPE_STEP_INGREDIENT = "entity_read_recipe_step_ingredient", diff --git a/src/services/command/entity.ts b/src/services/command/entity.ts index acd688c..603d4c9 100644 --- a/src/services/command/entity.ts +++ b/src/services/command/entity.ts @@ -3,11 +3,6 @@ import type { IngredientInterface, IngredientUpdateInterface, } from "../../types/entity/ingredient-interface.ts"; -import type { - RecipeFileCreateInterface, - RecipeFileInterface, - RecipeFileUpdateInterface, -} from "../../types/entity/recipe-file-interface.ts"; import type { RecipeIngredientDraftCreateInterface, RecipeIngredientDraftInterface, @@ -18,6 +13,11 @@ import type { RecipeInterface, RecipeUpdateInterface, } from "../../types/entity/recipe-interface.ts"; +import type { + RecipeStepFileCreateInterface, + RecipeStepFileInterface, + RecipeStepFileUpdateInterface, +} from "../../types/entity/recipe-step-file-interface.ts"; import type { RecipeStepIngredientDraftCreateInterface, RecipeStepIngredientDraftInterface, @@ -42,10 +42,6 @@ import type { IngredientCondition, IngredientFilter, } from "../../types/filter/ingredient-filter.ts"; -import type { - RecipeFileCondition, - RecipeFileFilter, -} from "../../types/filter/recipe-file-filter.ts"; import type { RecipeCondition, RecipeFilter, @@ -54,6 +50,10 @@ import type { RecipeIngredientDraftCondition, RecipeIngredientDraftFilter, } from "../../types/filter/recipe-ingredient-draft-filter.ts"; +import type { + RecipeStepFileCondition, + RecipeStepFileFilter, +} from "../../types/filter/recipe-step-file-filter.ts"; import type { RecipeStepCondition, RecipeStepFilter, @@ -80,7 +80,7 @@ type CommandEntityRead = | Command.ENTITY_READ_INGREDIENT | Command.ENTITY_READ_RECIPE | Command.ENTITY_READ_RECIPE_INGREDIENT_DRAFT - | Command.ENTITY_READ_RECIPE_FILE + | Command.ENTITY_READ_RECIPE_STEP_FILE | Command.ENTITY_READ_RECIPE_STEP_INGREDIENT | Command.ENTITY_READ_RECIPE_STEP_INGREDIENT_DRAFT | Command.ENTITY_READ_RECIPE_STEP @@ -90,7 +90,7 @@ type CommandEntityList = | Command.ENTITY_LIST_INGREDIENT | Command.ENTITY_LIST_RECIPE | Command.ENTITY_LIST_RECIPE_INGREDIENT_DRAFT - | Command.ENTITY_LIST_RECIPE_FILE + | Command.ENTITY_LIST_RECIPE_STEP_FILE | Command.ENTITY_LIST_RECIPE_STEP_INGREDIENT | Command.ENTITY_LIST_RECIPE_STEP_INGREDIENT_DRAFT | Command.ENTITY_LIST_RECIPE_STEP @@ -100,7 +100,7 @@ type CommandEntityCount = | Command.ENTITY_COUNT_INGREDIENT | Command.ENTITY_COUNT_RECIPE | Command.ENTITY_COUNT_RECIPE_INGREDIENT_DRAFT - | Command.ENTITY_COUNT_RECIPE_FILE + | Command.ENTITY_COUNT_RECIPE_STEP_FILE | Command.ENTITY_COUNT_RECIPE_STEP_INGREDIENT | Command.ENTITY_COUNT_RECIPE_STEP_INGREDIENT_DRAFT | Command.ENTITY_COUNT_RECIPE_STEP @@ -114,7 +114,7 @@ const entityReadPromiseCollector: { [Command.ENTITY_READ_INGREDIENT]: {}, [Command.ENTITY_READ_RECIPE]: {}, [Command.ENTITY_READ_RECIPE_INGREDIENT_DRAFT]: {}, - [Command.ENTITY_READ_RECIPE_FILE]: {}, + [Command.ENTITY_READ_RECIPE_STEP_FILE]: {}, [Command.ENTITY_READ_RECIPE_STEP_INGREDIENT]: {}, [Command.ENTITY_READ_RECIPE_STEP_INGREDIENT_DRAFT]: {}, [Command.ENTITY_READ_RECIPE_STEP]: {}, @@ -129,7 +129,7 @@ const entityListPromiseCollector: { [Command.ENTITY_LIST_INGREDIENT]: {}, [Command.ENTITY_LIST_RECIPE]: {}, [Command.ENTITY_LIST_RECIPE_INGREDIENT_DRAFT]: {}, - [Command.ENTITY_LIST_RECIPE_FILE]: {}, + [Command.ENTITY_LIST_RECIPE_STEP_FILE]: {}, [Command.ENTITY_LIST_RECIPE_STEP_INGREDIENT]: {}, [Command.ENTITY_LIST_RECIPE_STEP_INGREDIENT_DRAFT]: {}, [Command.ENTITY_LIST_RECIPE_STEP]: {}, @@ -144,7 +144,7 @@ const entityCountPromiseCollector: { [Command.ENTITY_COUNT_INGREDIENT]: {}, [Command.ENTITY_COUNT_RECIPE]: {}, [Command.ENTITY_COUNT_RECIPE_INGREDIENT_DRAFT]: {}, - [Command.ENTITY_COUNT_RECIPE_FILE]: {}, + [Command.ENTITY_COUNT_RECIPE_STEP_FILE]: {}, [Command.ENTITY_COUNT_RECIPE_STEP_INGREDIENT]: {}, [Command.ENTITY_COUNT_RECIPE_STEP_INGREDIENT_DRAFT]: {}, [Command.ENTITY_COUNT_RECIPE_STEP]: {}, @@ -306,34 +306,38 @@ export function countRecipeIngredientDraft( ); } -export function createRecipeFile( - create: RecipeFileCreateInterface, +export function createRecipeStepFile( + create: RecipeStepFileCreateInterface, ): Promise { - return invoke(Command.ENTITY_CREATE_RECIPE_FILE, { create }); + return invoke(Command.ENTITY_CREATE_RECIPE_STEP_FILE, { create }); } -export function readRecipeFile(id: number): Promise { - return readCollected(Command.ENTITY_READ_RECIPE_FILE, id); +export function readRecipeStepFile( + id: number, +): Promise { + return readCollected(Command.ENTITY_READ_RECIPE_STEP_FILE, id); } -export function updateRecipeFile( - update: RecipeFileUpdateInterface, +export function updateRecipeStepFile( + update: RecipeStepFileUpdateInterface, ): Promise { - return invoke(Command.ENTITY_UPDATE_RECIPE_FILE, { update }); + return invoke(Command.ENTITY_UPDATE_RECIPE_STEP_FILE, { update }); } -export function deleteRecipeFile(id: number): Promise { - return invoke(Command.ENTITY_DELETE_RECIPE_FILE, { id }); +export function deleteRecipeStepFile(id: number): Promise { + return invoke(Command.ENTITY_DELETE_RECIPE_STEP_FILE, { id }); } -export function listRecipeFile(filter: RecipeFileFilter): Promise { - return listCollected(Command.ENTITY_LIST_RECIPE_FILE, filter); +export function listRecipeStepFile( + filter: RecipeStepFileFilter, +): Promise { + return listCollected(Command.ENTITY_LIST_RECIPE_STEP_FILE, filter); } -export function countRecipeFile( - condition?: RecipeFileCondition, +export function countRecipeStepFile( + condition?: RecipeStepFileCondition, ): Promise { - return countCollected(Command.ENTITY_COUNT_RECIPE_FILE, condition); + return countCollected(Command.ENTITY_COUNT_RECIPE_STEP_FILE, condition); } export function createRecipeStepIngredient( diff --git a/src/services/event/event-channel.ts b/src/services/event/event-channel.ts index cfed483..04ce85d 100644 --- a/src/services/event/event-channel.ts +++ b/src/services/event/event-channel.ts @@ -8,9 +8,9 @@ export const enum EventChannel { ENTITY_ACTION_CREATED_RECIPE_INGREDIENT_DRAFT = "ENTITY_ACTION_CREATED_RECIPE_INGREDIENT_DRAFT", ENTITY_ACTION_UPDATED_RECIPE_INGREDIENT_DRAFT = "ENTITY_ACTION_UPDATED_RECIPE_INGREDIENT_DRAFT", ENTITY_ACTION_DELETED_RECIPE_INGREDIENT_DRAFT = "ENTITY_ACTION_DELETED_RECIPE_INGREDIENT_DRAFT", - ENTITY_ACTION_CREATED_RECIPE_FILE = "ENTITY_ACTION_CREATED_RECIPE_FILE", - ENTITY_ACTION_UPDATED_RECIPE_FILE = "ENTITY_ACTION_UPDATED_RECIPE_FILE", - ENTITY_ACTION_DELETED_RECIPE_FILE = "ENTITY_ACTION_DELETED_RECIPE_FILE", + ENTITY_ACTION_CREATED_RECIPE_STEP_FILE = "ENTITY_ACTION_CREATED_RECIPE_STEP_FILE", + ENTITY_ACTION_UPDATED_RECIPE_STEP_FILE = "ENTITY_ACTION_UPDATED_RECIPE_STEP_FILE", + ENTITY_ACTION_DELETED_RECIPE_STEP_FILE = "ENTITY_ACTION_DELETED_RECIPE_STEP_FILE", ENTITY_ACTION_CREATED_RECIPE_STEP_INGREDIENT = "ENTITY_ACTION_CREATED_RECIPE_STEP_INGREDIENT", ENTITY_ACTION_UPDATED_RECIPE_STEP_INGREDIENT = "ENTITY_ACTION_UPDATED_RECIPE_STEP_INGREDIENT", ENTITY_ACTION_DELETED_RECIPE_STEP_INGREDIENT = "ENTITY_ACTION_DELETED_RECIPE_STEP_INGREDIENT", diff --git a/src/services/event/event-payload.ts b/src/services/event/event-payload.ts index 08f56d6..edd46b1 100644 --- a/src/services/event/event-payload.ts +++ b/src/services/event/event-payload.ts @@ -10,9 +10,9 @@ type EventPayloadMap = { [EventChannel.ENTITY_ACTION_CREATED_RECIPE_INGREDIENT_DRAFT]: void; [EventChannel.ENTITY_ACTION_UPDATED_RECIPE_INGREDIENT_DRAFT]: number; [EventChannel.ENTITY_ACTION_DELETED_RECIPE_INGREDIENT_DRAFT]: number; - [EventChannel.ENTITY_ACTION_CREATED_RECIPE_FILE]: void; - [EventChannel.ENTITY_ACTION_UPDATED_RECIPE_FILE]: number; - [EventChannel.ENTITY_ACTION_DELETED_RECIPE_FILE]: number; + [EventChannel.ENTITY_ACTION_CREATED_RECIPE_STEP_FILE]: void; + [EventChannel.ENTITY_ACTION_UPDATED_RECIPE_STEP_FILE]: number; + [EventChannel.ENTITY_ACTION_DELETED_RECIPE_STEP_FILE]: number; [EventChannel.ENTITY_ACTION_CREATED_RECIPE_STEP_INGREDIENT]: void; [EventChannel.ENTITY_ACTION_UPDATED_RECIPE_STEP_INGREDIENT]: number; [EventChannel.ENTITY_ACTION_DELETED_RECIPE_STEP_INGREDIENT]: number; diff --git a/src/services/store/repository/recipe-file-repository.ts b/src/services/store/repository/recipe-file-repository.ts deleted file mode 100644 index 2a80b07..0000000 --- a/src/services/store/repository/recipe-file-repository.ts +++ /dev/null @@ -1,52 +0,0 @@ -import type { - RecipeFileCreateInterface, - RecipeFileInterface, - RecipeFileUpdateInterface, -} from "../../../types/entity/recipe-file-interface.ts"; -import type { - RecipeFileCondition, - RecipeFileOrderBy, -} from "../../../types/filter/recipe-file-filter.ts"; -import { - countRecipeFile, - createRecipeFile, - deleteRecipeFile, - listRecipeFile, - readRecipeFile, - updateRecipeFile, -} from "../../command/entity.ts"; -import { listen } from "../../event/client.ts"; -import { EventChannel } from "../../event/event-channel.ts"; -import { EntityRepository } from "./entity-repository.ts"; - -export const recipeFileRepository: EntityRepository< - RecipeFileInterface, - RecipeFileCreateInterface, - RecipeFileUpdateInterface, - RecipeFileCondition, - RecipeFileOrderBy -> = new EntityRepository( - (entityCreate) => createRecipeFile(entityCreate), - (identifier) => readRecipeFile(identifier), - (entityUpdate) => updateRecipeFile(entityUpdate), - (identifier) => deleteRecipeFile(identifier), - (filter) => listRecipeFile(filter), - (condition) => countRecipeFile(condition), - undefined, - undefined, - (reactFunction) => { - void listen(EventChannel.ENTITY_ACTION_UPDATED_RECIPE_FILE, (event) => { - reactFunction(event.payload); - }); - }, - (reactFunction) => { - void listen(EventChannel.ENTITY_ACTION_CREATED_RECIPE_FILE, () => { - reactFunction(); - }); - }, - (reactFunction) => { - void listen(EventChannel.ENTITY_ACTION_DELETED_RECIPE_FILE, (event) => { - reactFunction(event.payload); - }); - }, -); diff --git a/src/services/store/repository/recipe-step-file-repository.ts b/src/services/store/repository/recipe-step-file-repository.ts new file mode 100644 index 0000000..938a4d4 --- /dev/null +++ b/src/services/store/repository/recipe-step-file-repository.ts @@ -0,0 +1,58 @@ +import type { + RecipeStepFileCreateInterface, + RecipeStepFileInterface, + RecipeStepFileUpdateInterface, +} from "../../../types/entity/recipe-step-file-interface.ts"; +import type { + RecipeStepFileCondition, + RecipeStepFileOrderBy, +} from "../../../types/filter/recipe-step-file-filter.ts"; +import { + countRecipeStepFile, + createRecipeStepFile, + deleteRecipeStepFile, + listRecipeStepFile, + readRecipeStepFile, + updateRecipeStepFile, +} from "../../command/entity.ts"; +import { listen } from "../../event/client.ts"; +import { EventChannel } from "../../event/event-channel.ts"; +import { EntityRepository } from "./entity-repository.ts"; + +export const recipeStepFileRepository: EntityRepository< + RecipeStepFileInterface, + RecipeStepFileCreateInterface, + RecipeStepFileUpdateInterface, + RecipeStepFileCondition, + RecipeStepFileOrderBy +> = new EntityRepository( + (entityCreate) => createRecipeStepFile(entityCreate), + (identifier) => readRecipeStepFile(identifier), + (entityUpdate) => updateRecipeStepFile(entityUpdate), + (identifier) => deleteRecipeStepFile(identifier), + (filter) => listRecipeStepFile(filter), + (condition) => countRecipeStepFile(condition), + undefined, + undefined, + (reactFunction) => { + void listen( + EventChannel.ENTITY_ACTION_UPDATED_RECIPE_STEP_FILE, + (event) => { + reactFunction(event.payload); + }, + ); + }, + (reactFunction) => { + void listen(EventChannel.ENTITY_ACTION_CREATED_RECIPE_STEP_FILE, () => { + reactFunction(); + }); + }, + (reactFunction) => { + void listen( + EventChannel.ENTITY_ACTION_DELETED_RECIPE_STEP_FILE, + (event) => { + reactFunction(event.payload); + }, + ); + }, +); diff --git a/src/services/translation/en.ts b/src/services/translation/en.ts index c119206..1b8a200 100644 --- a/src/services/translation/en.ts +++ b/src/services/translation/en.ts @@ -25,7 +25,7 @@ const translationStrings: TranslationStrings = { recipe: { name: "Recipe Name", }, - recipeFile: { + recipeStepFile: { name: "Name", path: "File", }, diff --git a/src/services/translation/translations.ts b/src/services/translation/translations.ts index 9e787ae..b4013c8 100644 --- a/src/services/translation/translations.ts +++ b/src/services/translation/translations.ts @@ -24,7 +24,7 @@ type Translations = { recipe: { name: T; }; - recipeFile: { + recipeStepFile: { name: T; path: T; }; diff --git a/src/types/entity/recipe-file-interface.ts b/src/types/entity/recipe-step-file-interface.ts similarity index 62% rename from src/types/entity/recipe-file-interface.ts rename to src/types/entity/recipe-step-file-interface.ts index 4632fcb..673d6d1 100644 --- a/src/types/entity/recipe-file-interface.ts +++ b/src/types/entity/recipe-step-file-interface.ts @@ -4,7 +4,7 @@ import type { SortableUpdateInterface, } from "../sortable-interface.ts"; -export interface RecipeFileInterface +export interface RecipeStepFileInterface extends IdentifiableInterface, SortableInterface { name: string; @@ -13,15 +13,15 @@ export interface RecipeFileInterface recipeStepId: number; } -type RecipeFileCreateUri = { path: string } | { url: string }; +type RecipeStepFileCreateUri = { path: string } | { url: string }; -export interface RecipeFileCreateInterface extends SortableInterface { +export interface RecipeStepFileCreateInterface extends SortableInterface { name: string; - uri: RecipeFileCreateUri; + uri: RecipeStepFileCreateUri; recipeStepId: number; } -export interface RecipeFileUpdateInterface +export interface RecipeStepFileUpdateInterface extends IdentifiableInterface, SortableUpdateInterface { name?: string; diff --git a/src/types/filter/recipe-file-filter.ts b/src/types/filter/recipe-file-filter.ts deleted file mode 100644 index 459fd71..0000000 --- a/src/types/filter/recipe-file-filter.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { FilterInterface, Order } from "../filter-interface.ts"; - -export type RecipeFileCondition = { - recipeStepId?: number; -}; - -export type RecipeFileOrderBy = { order: Order }; - -export type RecipeFileFilter = FilterInterface< - RecipeFileCondition, - RecipeFileOrderBy ->; diff --git a/src/types/filter/recipe-step-file-filter.ts b/src/types/filter/recipe-step-file-filter.ts new file mode 100644 index 0000000..ae46594 --- /dev/null +++ b/src/types/filter/recipe-step-file-filter.ts @@ -0,0 +1,12 @@ +import type { FilterInterface, Order } from "../filter-interface.ts"; + +export type RecipeStepFileCondition = { + recipeStepId?: number; +}; + +export type RecipeStepFileOrderBy = { order: Order }; + +export type RecipeStepFileFilter = FilterInterface< + RecipeStepFileCondition, + RecipeStepFileOrderBy +>;