From 5fb1eec3531f65bc893eac03370187af41dd18b5 Mon Sep 17 00:00:00 2001 From: Andrei Date: Wed, 26 Feb 2025 00:40:55 +1300 Subject: [PATCH 1/4] Rename requisition transform plugin and expose plugin data query to plugin --- client/packages/common/src/plugins/types.ts | 3 +- .../backendTypes/generated/EqualFilter.ts | 3 ++ .../generated/PluginDataFilter.ts | 4 ++ .../backendTypes/generated/PluginTypes.ts | 8 ++-- .../TransformRequestRequisitionLineInput.ts | 5 +++ .../TransformRequestRequisitionLineOutput.ts | 5 +++ client/packages/plugins/backendTypes/types.ts | 9 ++-- .../src/db_diesel/backend_plugin_row.rs | 16 +++++--- .../src/db_diesel/filter_sort_pagination.rs | 9 +++- .../repository/src/db_diesel/plugin_data.rs | 10 ++++- .../src/backend_plugin/boajs/call_plugin.rs | 18 +++++--- .../boajs/methods/get_plugin_data.rs | 35 ++++++++++++++++ .../src/backend_plugin/boajs/methods/mod.rs | 1 + .../service/src/backend_plugin/boajs/utils.rs | 41 +++++++++++++++---- .../src/backend_plugin/plugin_provider.rs | 17 ++++---- .../service/src/backend_plugin/types/amc.rs | 7 +++- .../service/src/backend_plugin/types/mod.rs | 14 +++++-- ...=> transform_request_requisition_lines.rs} | 13 ++++-- server/service/src/item_stats.rs | 2 +- .../add_from_master_list.rs | 3 ++ .../request_requisition/generate.rs | 15 ++++--- .../request_requisition/insert_program.rs | 11 ++++- .../request_requisition/update/generate.rs | 8 ++-- .../request_requisition_line/insert.rs | 11 +++-- server/service/src/sync/mod.rs | 9 ++-- .../src/sync/test/test_data/backend_plugin.rs | 4 +- 26 files changed, 215 insertions(+), 66 deletions(-) create mode 100644 client/packages/plugins/backendTypes/generated/EqualFilter.ts create mode 100644 client/packages/plugins/backendTypes/generated/PluginDataFilter.ts create mode 100644 client/packages/plugins/backendTypes/generated/TransformRequestRequisitionLineInput.ts create mode 100644 client/packages/plugins/backendTypes/generated/TransformRequestRequisitionLineOutput.ts create mode 100644 server/service/src/backend_plugin/boajs/methods/get_plugin_data.rs rename server/service/src/backend_plugin/types/{transform_requisition_lines.rs => transform_request_requisition_lines.rs} (66%) diff --git a/client/packages/common/src/plugins/types.ts b/client/packages/common/src/plugins/types.ts index 083c360f1c..70ec2852a7 100644 --- a/client/packages/common/src/plugins/types.ts +++ b/client/packages/common/src/plugins/types.ts @@ -20,7 +20,8 @@ export type Plugins = { }; requestRequisitionColumn?: { StateLoader: React.ComponentType<{ requestLines: RequestLineFragment[] }>[]; - columns: ColumnDefinition[]; + tableColumns: ColumnDefinition[]; + editViewColumns: React.ComponentType<{ line: RequestLineFragment }>[]; }; }; diff --git a/client/packages/plugins/backendTypes/generated/EqualFilter.ts b/client/packages/plugins/backendTypes/generated/EqualFilter.ts new file mode 100644 index 0000000000..e047609ae5 --- /dev/null +++ b/client/packages/plugins/backendTypes/generated/EqualFilter.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type EqualFilter = { equal_to?: T, not_equal_to?: T, equal_any?: Array, equal_any_or_null?: Array, not_equal_all?: Array, is_null?: boolean, }; diff --git a/client/packages/plugins/backendTypes/generated/PluginDataFilter.ts b/client/packages/plugins/backendTypes/generated/PluginDataFilter.ts new file mode 100644 index 0000000000..84ad786b5c --- /dev/null +++ b/client/packages/plugins/backendTypes/generated/PluginDataFilter.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { EqualFilter } from "./EqualFilter"; + +export type PluginDataFilter = { id?: EqualFilter, plugin_code?: EqualFilter, related_record_id?: EqualFilter, data_identifier?: EqualFilter, store_id?: EqualFilter, }; diff --git a/client/packages/plugins/backendTypes/generated/PluginTypes.ts b/client/packages/plugins/backendTypes/generated/PluginTypes.ts index cfc7c41331..480b85b449 100644 --- a/client/packages/plugins/backendTypes/generated/PluginTypes.ts +++ b/client/packages/plugins/backendTypes/generated/PluginTypes.ts @@ -2,8 +2,10 @@ import type { AverageMonthlyConsumptionInput } from "./AverageMonthlyConsumptionInput"; import type { AverageMonthlyConsumptionItem } from "./AverageMonthlyConsumptionItem"; import type { Function } from "./Function"; +import type { PluginDataFilter } from "./PluginDataFilter"; +import type { PluginDataRow } from "./PluginDataRow"; import type { StorePreferenceRow } from "./StorePreferenceRow"; -import type { TransformRequisitionLineInput } from "./TransformRequisitionLineInput"; -import type { TransformRequisitionLineOutput } from "./TransformRequisitionLineOutput"; +import type { TransformRequestRequisitionLineInput } from "./TransformRequestRequisitionLineInput"; +import type { TransformRequestRequisitionLineOutput } from "./TransformRequestRequisitionLineOutput"; -export type PluginTypes = { average_monthly_consumption: Function, transform_requisition_lines: Function, get_store_preferences: StorePreferenceRow, }; +export type PluginTypes = { average_monthly_consumption: Function, transform_request_requisition_lines: Function, get_store_preferences: StorePreferenceRow, get_plugin_data: Function>, }; diff --git a/client/packages/plugins/backendTypes/generated/TransformRequestRequisitionLineInput.ts b/client/packages/plugins/backendTypes/generated/TransformRequestRequisitionLineInput.ts new file mode 100644 index 0000000000..2d1b2b9573 --- /dev/null +++ b/client/packages/plugins/backendTypes/generated/TransformRequestRequisitionLineInput.ts @@ -0,0 +1,5 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { RequisitionLineRow } from "./RequisitionLineRow"; +import type { RequisitionRow } from "./RequisitionRow"; + +export type TransformRequestRequisitionLineInput = { requisition: RequisitionRow, lines: Array, }; diff --git a/client/packages/plugins/backendTypes/generated/TransformRequestRequisitionLineOutput.ts b/client/packages/plugins/backendTypes/generated/TransformRequestRequisitionLineOutput.ts new file mode 100644 index 0000000000..58e08f7ff4 --- /dev/null +++ b/client/packages/plugins/backendTypes/generated/TransformRequestRequisitionLineOutput.ts @@ -0,0 +1,5 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PluginDataRow } from "./PluginDataRow"; +import type { RequisitionLineRow } from "./RequisitionLineRow"; + +export type TransformRequestRequisitionLineOutput = { transformed_lines: Array, plugin_data?: Array, }; diff --git a/client/packages/plugins/backendTypes/types.ts b/client/packages/plugins/backendTypes/types.ts index 81165f7e2d..7c8c6dc68d 100644 --- a/client/packages/plugins/backendTypes/types.ts +++ b/client/packages/plugins/backendTypes/types.ts @@ -4,9 +4,9 @@ export type BackendPlugins = { average_monthly_consumption?: ( _: PluginTypes['average_monthly_consumption']['input'] ) => PluginTypes['average_monthly_consumption']['output']; - transform_requisition_lines?: ( - _: PluginTypes['transform_requisition_lines']['input'] - ) => PluginTypes['transform_requisition_lines']['output']; + transform_request_requisition_lines?: ( + _: PluginTypes['transform_request_requisition_lines']['input'] + ) => PluginTypes['transform_request_requisition_lines']['output']; }; declare global { @@ -15,4 +15,7 @@ declare global { var get_store_preferences: ( _: string ) => PluginTypes['get_store_preferences']; + var get_plugin_data: ( + _: PluginTypes['get_plugin_data']['input'] + ) => PluginTypes['get_plugin_data']['output']; } diff --git a/server/repository/src/db_diesel/backend_plugin_row.rs b/server/repository/src/db_diesel/backend_plugin_row.rs index 8d8b17c7db..005d05007c 100644 --- a/server/repository/src/db_diesel/backend_plugin_row.rs +++ b/server/repository/src/db_diesel/backend_plugin_row.rs @@ -8,10 +8,10 @@ use diesel_derive_enum::DbEnum; use serde::{Deserialize, Serialize}; #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +#[serde(rename_all = "snake_case")] pub enum PluginType { - Amc, - TransformRequisitionLines, + AverageMonthlyConsumption, + TransformRequestRequisitionLines, } #[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize)] @@ -201,7 +201,10 @@ mod test { let repo = BackendPluginRowRepository::new(&connection); let id = "backend_plugin_row".to_string(); - let types = PluginTypes(vec![PluginType::Amc, PluginType::Amc]); + let types = PluginTypes(vec![ + PluginType::AverageMonthlyConsumption, + PluginType::AverageMonthlyConsumption, + ]); let _ = repo.upsert_one(BackendPluginRow { id: id.clone(), types: types.clone(), @@ -216,6 +219,9 @@ mod test { .unwrap(); // Showing that types serializes to a readable text in DB field - assert_eq!(result[0].types, r#"["AMC","AMC"]"#); + assert_eq!( + result[0].types, + r#"["average_monthly_consumption","average_monthly_consumption"]"# + ); } } diff --git a/server/repository/src/db_diesel/filter_sort_pagination.rs b/server/repository/src/db_diesel/filter_sort_pagination.rs index 8702ccc715..b42a62a8cb 100644 --- a/server/repository/src/db_diesel/filter_sort_pagination.rs +++ b/server/repository/src/db_diesel/filter_sort_pagination.rs @@ -2,6 +2,7 @@ use std::ops::Range; use chrono::{NaiveDate, NaiveDateTime}; use serde::{Deserialize, Serialize}; +use ts_rs::TS; use util::inline_init; #[derive(Clone, PartialEq, Debug, Default)] @@ -101,16 +102,22 @@ impl StringFilter { } } -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] +#[derive(Clone, PartialEq, Debug, TS, Serialize, Deserialize)] pub struct EqualFilter where T: 'static, { + #[ts(optional)] pub equal_to: Option, + #[ts(optional)] pub not_equal_to: Option, + #[ts(optional)] pub equal_any: Option>, + #[ts(optional)] pub equal_any_or_null: Option>, + #[ts(optional)] pub not_equal_all: Option>, + #[ts(optional)] pub is_null: Option, } diff --git a/server/repository/src/db_diesel/plugin_data.rs b/server/repository/src/db_diesel/plugin_data.rs index ed868a525d..953362f9e3 100644 --- a/server/repository/src/db_diesel/plugin_data.rs +++ b/server/repository/src/db_diesel/plugin_data.rs @@ -4,20 +4,26 @@ use crate::{ diesel_macros::{apply_equal_filter, apply_sort_no_case}, DBType, EqualFilter, Pagination, PluginDataRow, RepositoryError, Sort, }; - use diesel::prelude::*; +use serde::{Deserialize, Serialize}; +use ts_rs::TS; #[derive(Debug, Clone, PartialEq)] pub struct PluginData { pub plugin_data: PluginDataRow, } -#[derive(Clone, PartialEq, Debug, Default)] +#[derive(Clone, PartialEq, Debug, Default, TS, Serialize, Deserialize)] pub struct PluginDataFilter { + #[ts(optional)] pub id: Option>, + #[ts(optional)] pub plugin_code: Option>, + #[ts(optional)] pub related_record_id: Option>, + #[ts(optional)] pub data_identifier: Option>, + #[ts(optional)] pub store_id: Option>, } diff --git a/server/service/src/backend_plugin/boajs/call_plugin.rs b/server/service/src/backend_plugin/boajs/call_plugin.rs index fe3c3b05f4..a4efc90e61 100644 --- a/server/service/src/backend_plugin/boajs/call_plugin.rs +++ b/server/service/src/backend_plugin/boajs/call_plugin.rs @@ -5,6 +5,7 @@ use boa_engine::{ JsValue, Module, Source, }; +use repository::PluginType; use serde::{de::DeserializeOwned, Serialize}; use thiserror::Error; @@ -30,9 +31,13 @@ impl PartialEq for BoaJsPluginError { } } +fn plugin_type_to_string(r#type: &PluginType) -> String { + serde_json::to_string(r#type).unwrap().replace("\"", "") +} + pub(crate) fn call_plugin( input: I, - name: &str, + r#type: &PluginType, bundle: &Vec, ) -> Result where @@ -40,6 +45,8 @@ where O: DeserializeOwned, { use BoaJsPluginError as Error; + let r#type = plugin_type_to_string(r#type); + // Initialise context with loader let loader = Rc::new(SimpleModuleLoader::new(Path::new("."))?); let mut context = &mut Context::builder().module_loader(loader.clone()).build()?; @@ -53,7 +60,7 @@ where context.run_jobs(); match promise.state() { PromiseState::Fulfilled(JsValue::Undefined) => {} - _ => return Err(Error::LoadingModule(name.to_string())), + _ => return Err(Error::LoadingModule(r#type.clone())), } // TODO should these be bound as camel case ? Also for inputs and outputs ? @@ -61,20 +68,21 @@ where methods::sql::bind_method(context)?; methods::sql_type::bind_method(context)?; methods::get_store_preferences::bind_method(context)?; + methods::get_plugin_data::bind_method(context)?; let namespace = module.namespace(context); let plugins = namespace .get(js_string!("plugins"), context)? .as_object() .cloned() - .ok_or_else(|| Error::PluginNamespaceMissing(name.to_string()))?; + .ok_or_else(|| Error::PluginNamespaceMissing(r#type.clone()))?; - let key = js_string!(name); + let key = js_string!(r#type.as_str()); let plugin = plugins .get(key, context)? .as_callable() .cloned() - .ok_or_else(|| Error::PluginMissing(name.to_string()))?; + .ok_or_else(|| Error::PluginMissing(r#type.clone()))?; let input: serde_json::Value = serde_json::to_value(&input)?; let js_input = JsValue::from_json(&input, &mut context)?; diff --git a/server/service/src/backend_plugin/boajs/methods/get_plugin_data.rs b/server/service/src/backend_plugin/boajs/methods/get_plugin_data.rs new file mode 100644 index 0000000000..b552c1f289 --- /dev/null +++ b/server/service/src/backend_plugin/boajs/methods/get_plugin_data.rs @@ -0,0 +1,35 @@ +use boa_engine::*; +use repository::{PluginDataFilter, PluginDataRepository, PluginDataRow}; + +use crate::backend_plugin::{boajs::utils::*, plugin_provider::PluginContext}; + +pub(crate) fn bind_method(context: &mut Context) -> Result<(), JsError> { + context.register_global_callable( + JsString::from("get_plugin_data"), + 0, + NativeFunction::from_copy_closure(move |_, args, mut ctx| { + // TODO Is this actually safe ? (need to check reference counts after plugin has run) + let service_provider = PluginContext::service_provider(); + + let filter: PluginDataFilter = get_serde_argument(&mut ctx, args, 0)?; + + let connection = service_provider + .connection() + .map_err(std_error_to_js_error)?; + + // TODO pagination or restrictions ? + let plugin_data: Vec = PluginDataRepository::new(&connection) + .query_by_filter(filter) + .map_err(std_error_to_js_error)? + .into_iter() + .map(|r| r.plugin_data) + .collect(); + + let value: serde_json::Value = + serde_json::to_value(&plugin_data).map_err(std_error_to_js_error)?; + // We return the moved variable as a `JsValue`. + Ok(JsValue::from_json(&value, ctx)?) + }), + )?; + Ok(()) +} diff --git a/server/service/src/backend_plugin/boajs/methods/mod.rs b/server/service/src/backend_plugin/boajs/methods/mod.rs index bed0c7c030..a0d8919b75 100644 --- a/server/service/src/backend_plugin/boajs/methods/mod.rs +++ b/server/service/src/backend_plugin/boajs/methods/mod.rs @@ -1,3 +1,4 @@ +pub(crate) mod get_plugin_data; pub(crate) mod get_store_preferences; pub(crate) mod log; pub(crate) mod sql; diff --git a/server/service/src/backend_plugin/boajs/utils.rs b/server/service/src/backend_plugin/boajs/utils.rs index 8f366d7df2..253e201c34 100644 --- a/server/service/src/backend_plugin/boajs/utils.rs +++ b/server/service/src/backend_plugin/boajs/utils.rs @@ -1,6 +1,7 @@ use std::string::FromUtf16Error; -use boa_engine::{js_string, JsError, JsValue}; +use boa_engine::{js_string, Context, JsError, JsValue}; +use serde::de::DeserializeOwned; use std::error::Error as StandardError; use thiserror::Error; use util::format_error; @@ -19,13 +20,11 @@ pub(super) fn get_string_argument(args: &[JsValue], index: usize) -> Result Result { - let Some(arg) = args.get(index) else { - return Err(Error::NoArgumentAtIndex(index)); - }; + let arg = args.get(index).ok_or(Error::NoArgumentAtIndex(index))?; - let Some(arg) = arg.as_string() else { - return Err(Error::ArgumentAtIndexIsNotAString(index)); - }; + let arg = arg + .as_string() + .ok_or(Error::ArgumentAtIndexIsNotAString(index))?; Ok(arg.to_std_string()?) }; @@ -37,3 +36,31 @@ pub(super) fn std_error_to_js_error(error: impl StandardError) -> JsError { let as_string = JsValue::String(js_string!(format_error(&error))); JsError::from_opaque(as_string) } + +#[derive(Debug, Error)] +enum GetJsonArgumentError { + #[error("No argument at index {0}")] + NoArgumentAtIndex(usize), + #[error(transparent)] + JsError(#[from] JsError), + #[error(transparent)] + SerdeError(#[from] serde_json::Error), +} + +pub(super) fn get_serde_argument( + context: &mut Context, + args: &[JsValue], + index: usize, +) -> Result { + use GetJsonArgumentError as Error; + + let mut closure = move || -> Result { + let arg = args.get(index).ok_or(Error::NoArgumentAtIndex(index))?; + + let value = arg.to_json(context)?; + + Ok(serde_json::from_value(value)?) + }; + + closure().map_err(std_error_to_js_error) +} diff --git a/server/service/src/backend_plugin/plugin_provider.rs b/server/service/src/backend_plugin/plugin_provider.rs index da0efa5cd6..6912d3b40e 100644 --- a/server/service/src/backend_plugin/plugin_provider.rs +++ b/server/service/src/backend_plugin/plugin_provider.rs @@ -13,9 +13,9 @@ use crate::{backend_plugin::boajs, service_provider::ServiceProvider}; use super::boajs::BoaJsPluginError; #[derive(Debug, Error, PartialEq)] -#[error("Error in plugin {name}")] +#[error("Error in plugin {r#type:?}")] pub struct PluginError { - name: String, + r#type: PluginType, #[source] variant: PluginErrorVariant, } @@ -69,21 +69,22 @@ impl PluginContext { } } -pub(crate) fn call_plugin(input: I, name: &str, plugin: &PluginInstance) -> PluginResult +pub(crate) fn call_plugin( + input: I, + r#type: PluginType, + plugin: &PluginInstance, +) -> PluginResult where I: Serialize, O: DeserializeOwned, { let result = match plugin { PluginInstance::BoaJs(bundle) => { - boajs::call_plugin(input, name, &bundle).map_err(Into::into) + boajs::call_plugin(input, &r#type, &bundle).map_err(Into::into) } }; - result.map_err(|variant| PluginError { - name: name.to_string(), - variant, - }) + result.map_err(|variant| PluginError { r#type, variant }) } #[derive(Serialize, Deserialize, Default)] diff --git a/server/service/src/backend_plugin/types/amc.rs b/server/service/src/backend_plugin/types/amc.rs index 65818ed4b2..c46da91fb5 100644 --- a/server/service/src/backend_plugin/types/amc.rs +++ b/server/service/src/backend_plugin/types/amc.rs @@ -1,5 +1,6 @@ use crate::backend_plugin::{plugin_provider::PluginInstance, *}; use plugin_provider::{call_plugin, PluginResult}; +use repository::PluginType; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use ts_rs::TS; @@ -30,6 +31,10 @@ pub trait Trait: Send + Sync { // TODO as macro ? Can do types here too impl self::Trait for PluginInstance { fn call(&self, input: Input) -> PluginResult { - Ok(call_plugin(input, "average_monthly_consumption", self)?) + Ok(call_plugin( + input, + PluginType::AverageMonthlyConsumption, + self, + )?) } } diff --git a/server/service/src/backend_plugin/types/mod.rs b/server/service/src/backend_plugin/types/mod.rs index 793ae8a84d..9c9d756a45 100644 --- a/server/service/src/backend_plugin/types/mod.rs +++ b/server/service/src/backend_plugin/types/mod.rs @@ -1,10 +1,10 @@ pub mod amc; -pub mod transform_requisition_lines; +pub mod transform_request_requisition_lines; #[cfg(test)] mod generate_typescript_types { use super::*; - use repository::StorePreferenceRow; + use repository::{PluginDataFilter, PluginDataRow, StorePreferenceRow}; use ts_rs::TS; #[derive(TS)] @@ -17,10 +17,16 @@ mod generate_typescript_types { #[derive(TS)] #[allow(unused)] struct PluginTypes { + // Fields here must match PluginTypes in backend_plugin_row repository average_monthly_consumption: Function, - transform_requisition_lines: - Function, + transform_request_requisition_lines: Function< + transform_request_requisition_lines::Input, + transform_request_requisition_lines::Output, + >, + // Extra types to expose, not directly related to plugin interface + // like for input or output of global methods get_store_preferences: StorePreferenceRow, + get_plugin_data: Function>, } #[test] diff --git a/server/service/src/backend_plugin/types/transform_requisition_lines.rs b/server/service/src/backend_plugin/types/transform_request_requisition_lines.rs similarity index 66% rename from server/service/src/backend_plugin/types/transform_requisition_lines.rs rename to server/service/src/backend_plugin/types/transform_request_requisition_lines.rs index 59f4cbe0ec..0538e9655f 100644 --- a/server/service/src/backend_plugin/types/transform_requisition_lines.rs +++ b/server/service/src/backend_plugin/types/transform_request_requisition_lines.rs @@ -1,20 +1,21 @@ use crate::backend_plugin::{plugin_provider::PluginInstance, *}; use plugin_provider::{call_plugin, PluginResult}; -use repository::{PluginDataRow, RequisitionLineRow, RequisitionRow}; +use repository::{PluginDataRow, PluginType, RequisitionLineRow, RequisitionRow}; use serde::{Deserialize, Serialize}; use ts_rs::TS; #[derive(TS, Clone, Deserialize, Serialize)] -#[ts(rename = "TransformRequisitionLineInput")] +#[ts(rename = "TransformRequestRequisitionLineInput")] pub struct Input { pub requisition: RequisitionRow, pub lines: Vec, } #[derive(TS, Clone, Deserialize, Serialize)] -#[ts(rename = "TransformRequisitionLineOutput")] +#[ts(rename = "TransformRequestRequisitionLineOutput")] pub struct Output { pub transformed_lines: Vec, + #[ts(optional)] pub plugin_data: Option>, } pub trait Trait: Send + Sync { @@ -23,6 +24,10 @@ pub trait Trait: Send + Sync { impl self::Trait for PluginInstance { fn call(&self, input: Input) -> PluginResult { - Ok(call_plugin(input, "transform_requisition_lines", self)?) + Ok(call_plugin( + input, + PluginType::TransformRequestRequisitionLines, + self, + )?) } } diff --git a/server/service/src/item_stats.rs b/server/service/src/item_stats.rs index c7c0ff69ec..3008db2a39 100644 --- a/server/service/src/item_stats.rs +++ b/server/service/src/item_stats.rs @@ -70,7 +70,7 @@ pub fn get_item_stats( item_ids: item_ids.clone(), }; - let amc_by_item = match PluginInstance::get_one(PluginType::Amc) { + let amc_by_item = match PluginInstance::get_one(PluginType::AverageMonthlyConsumption) { Some(plugin) => amc::Trait::call(&(*plugin), input), None => amc::Trait::call(&DefaultAmc, input), }?; diff --git a/server/service/src/requisition/request_requisition/add_from_master_list.rs b/server/service/src/requisition/request_requisition/add_from_master_list.rs index 49b04e34f8..a398fd1048 100644 --- a/server/service/src/requisition/request_requisition/add_from_master_list.rs +++ b/server/service/src/requisition/request_requisition/add_from_master_list.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use crate::{ backend_plugin::plugin_provider::PluginError, requisition::common::{check_requisition_row_exists, get_lines_for_requisition}, @@ -127,6 +129,7 @@ fn generate( store_id, &requisition_row, items_ids_not_in_requisition, + HashMap::new(), ) } diff --git a/server/service/src/requisition/request_requisition/generate.rs b/server/service/src/requisition/request_requisition/generate.rs index 6708daad0c..e58bd3e64f 100644 --- a/server/service/src/requisition/request_requisition/generate.rs +++ b/server/service/src/requisition/request_requisition/generate.rs @@ -1,9 +1,12 @@ +use std::collections::HashMap; + use chrono::Utc; use repository::{PluginDataRow, PluginType, RequisitionLineRow, RequisitionRow}; use util::uuid::uuid; use crate::backend_plugin::plugin_provider::PluginInstance; -use crate::backend_plugin::types::transform_requisition_lines; + +use crate::backend_plugin::types::transform_request_requisition_lines; use crate::item_stats::get_item_stats; use crate::service_provider::ServiceContext; use crate::PluginOrRepositoryError; @@ -46,6 +49,8 @@ pub fn generate_requisition_lines( store_id: &str, requisition_row: &RequisitionRow, item_ids: Vec, + // Need for plugins since sometimes we set id passed in as parameter in graphql + mut with_id_map: HashMap, ) -> Result<(Vec, Vec), PluginOrRepositoryError> { let item_stats_rows = get_item_stats(ctx, store_id, None, item_ids)?; @@ -62,7 +67,7 @@ pub fn generate_requisition_lines( }); RequisitionLineRow { - id: uuid(), + id: with_id_map.remove(&item_stats.item_id).unwrap_or(uuid()), requisition_id: requisition_row.id.clone(), item_link_id: item_stats.item_id, item_name: item_stats.item_name, @@ -88,13 +93,13 @@ pub fn generate_requisition_lines( }) .collect(); - let Some(plugin) = PluginInstance::get_one(PluginType::TransformRequisitionLines) else { + let Some(plugin) = PluginInstance::get_one(PluginType::TransformRequestRequisitionLines) else { return Ok((lines, Vec::new())); }; - let result = transform_requisition_lines::Trait::call( + let result = transform_request_requisition_lines::Trait::call( &(*plugin), - transform_requisition_lines::Input { + transform_request_requisition_lines::Input { requisition: requisition_row.clone(), lines, }, diff --git a/server/service/src/requisition/request_requisition/insert_program.rs b/server/service/src/requisition/request_requisition/insert_program.rs index 03e3f78ac7..387afe43e8 100644 --- a/server/service/src/requisition/request_requisition/insert_program.rs +++ b/server/service/src/requisition/request_requisition/insert_program.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use crate::{ activity_log::activity_log_entry, backend_plugin::plugin_provider::PluginError, @@ -229,8 +231,13 @@ fn generate( .map(|line| line.item_id) .collect(); - let (requisition_lines, plugin_data_rows) = - generate_requisition_lines(ctx, &ctx.store_id, &requisition, program_item_ids)?; + let (requisition_lines, plugin_data_rows) = generate_requisition_lines( + ctx, + &ctx.store_id, + &requisition, + program_item_ids, + HashMap::new(), + )?; let program_indicators = program_indicators( connection, diff --git a/server/service/src/requisition/request_requisition/update/generate.rs b/server/service/src/requisition/request_requisition/update/generate.rs index 595b4b106f..ca87d171b9 100644 --- a/server/service/src/requisition/request_requisition/update/generate.rs +++ b/server/service/src/requisition/request_requisition/update/generate.rs @@ -1,6 +1,6 @@ use super::{UpdateRequestRequisition, UpdateRequestRequisitionStatus}; use crate::{ - backend_plugin::{plugin_provider::PluginInstance, types::transform_requisition_lines}, + backend_plugin::{plugin_provider::PluginInstance, types::transform_request_requisition_lines}, requisition::{ common::get_lines_for_requisition, request_requisition::{generate_suggested_quantity, GenerateSuggestedQuantity}, @@ -120,13 +120,13 @@ pub fn generate_updated_lines( ) .collect(); - let Some(plugin) = PluginInstance::get_one(PluginType::TransformRequisitionLines) else { + let Some(plugin) = PluginInstance::get_one(PluginType::TransformRequestRequisitionLines) else { return Ok((lines, Vec::new())); }; - let result = transform_requisition_lines::Trait::call( + let result = transform_request_requisition_lines::Trait::call( &(*plugin), - transform_requisition_lines::Input { + transform_request_requisition_lines::Input { requisition: requisition.clone(), lines, }, diff --git a/server/service/src/requisition_line/request_requisition_line/insert.rs b/server/service/src/requisition_line/request_requisition_line/insert.rs index 01842629ca..52cc052a23 100644 --- a/server/service/src/requisition_line/request_requisition_line/insert.rs +++ b/server/service/src/requisition_line/request_requisition_line/insert.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use crate::{ backend_plugin::plugin_provider::PluginError, item::item::check_item_exists, @@ -124,15 +126,16 @@ fn generate( item_id, }: InsertRequestRequisitionLine, ) -> Result<(RequisitionLineRow, Vec), OutError> { + let mut with_id_map = HashMap::new(); + with_id_map.insert(item_id.clone(), id); + let (mut requisition_lines, plugin_data) = - generate_requisition_lines(ctx, store_id, &requisition_row, vec![item_id])?; + generate_requisition_lines(ctx, store_id, &requisition_row, vec![item_id], with_id_map)?; - let mut new_requisition_line = requisition_lines + let new_requisition_line = requisition_lines .pop() .ok_or(OutError::CannotFindItemStatusForRequisitionLine)?; - new_requisition_line.id = id; - Ok((new_requisition_line, plugin_data)) } diff --git a/server/service/src/sync/mod.rs b/server/service/src/sync/mod.rs index 8c8aa8adbb..f4e88d07d0 100644 --- a/server/service/src/sync/mod.rs +++ b/server/service/src/sync/mod.rs @@ -118,10 +118,11 @@ impl CentralServerConfig { } pub fn is_central_server() -> bool { - CENTRAL_SERVER_CONFIG - .read() - .unwrap() - .inner_is_central_server() + true + // CENTRAL_SERVER_CONFIG + // .read() + // .unwrap() + // .inner_is_central_server() } pub fn get() -> Self { diff --git a/server/service/src/sync/test/test_data/backend_plugin.rs b/server/service/src/sync/test/test_data/backend_plugin.rs index efb31f2284..897724655b 100644 --- a/server/service/src/sync/test/test_data/backend_plugin.rs +++ b/server/service/src/sync/test/test_data/backend_plugin.rs @@ -12,7 +12,7 @@ const BACKEND_PLUGIN: (&str, &str) = ( "id": "backend_plugin", "bundle_base64": "bundle_base64", "code": "code", - "types": ["AMC"], + "types": ["average_monthly_consumption"], "variant_type": "BOA_JS" }"#, ); @@ -22,7 +22,7 @@ fn backend_plugin() -> BackendPluginRow { id: BACKEND_PLUGIN.0.to_string(), code: "code".to_string(), bundle_base64: "bundle_base64".to_string(), - types: PluginTypes(vec![PluginType::Amc]), + types: PluginTypes(vec![PluginType::AverageMonthlyConsumption]), variant_type: PluginVariantType::BoaJs, } } From 0a2f461a274bf2b4e9188d4aed84e0aec991e42f Mon Sep 17 00:00:00 2001 From: Andrei Date: Wed, 26 Feb 2025 00:44:47 +1300 Subject: [PATCH 2/4] Expose request line edit for front end column plugin --- client/packages/plugins/README.md | 4 ++-- .../DetailView/RequestLineEdit/RequestLineEdit.tsx | 9 +++++++++ .../src/RequestRequisition/DetailView/columns.ts | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/client/packages/plugins/README.md b/client/packages/plugins/README.md index 29ab8fee57..9097518ffb 100644 --- a/client/packages/plugins/README.md +++ b/client/packages/plugins/README.md @@ -173,7 +173,7 @@ You can work on plugins as if they were part of the app (types should be shared, ```bash # From server directory -cargo run --bin remote_server_cli -- generate-plugin-bundle -i ../client/packages/plugins/mynewplugin/frontend -o pluginbundle.json +cargo run --bin remote_server_cli -- generate-plugin-bundle -i ../client/packages/plugins/myPluginBundle/frontend -o pluginbundle.json ``` Above will generate `pluginbundle.json` with all backend and frontend plugins in the directory specified by `-i`, this bundle includes metadata, like code and plugin types and base64 contents of all of the files in the `dist` directory which was generated with `yarn build` command that was executed in every plugin directory. @@ -190,7 +190,7 @@ Note you must be uploading plugins to central server for this to work Alternatively one command can be used for both: ```bash -cargo run --bin remote_server_cli -- generate-and-install-plugin-bundle -i '../client/packages/plugins/mynewplugin/frontend' --url 'http://localhost:8000' --username admin --password pass +cargo run --bin remote_server_cli -- generate-and-install-plugin-bundle -i '../client/packages/plugins/myPluginBundle/frontend' --url 'http://localhost:8000' --username admin --password pass ``` In order to test this plugins in front end, you will need to start front end via `yarn -- -- --env LOAD_REMOTE_PLUGINS` which fetched plugins from the server rather then serving them from local directory, this is how plugins will be loaded in production (and plugins will sync and be served by remote site servers) diff --git a/client/packages/requisitions/src/RequestRequisition/DetailView/RequestLineEdit/RequestLineEdit.tsx b/client/packages/requisitions/src/RequestRequisition/DetailView/RequestLineEdit/RequestLineEdit.tsx index 46e40d8ef3..c03d8a4d0d 100644 --- a/client/packages/requisitions/src/RequestRequisition/DetailView/RequestLineEdit/RequestLineEdit.tsx +++ b/client/packages/requisitions/src/RequestRequisition/DetailView/RequestLineEdit/RequestLineEdit.tsx @@ -19,6 +19,7 @@ import { TextArea, useAuthContext, useNavigate, + usePluginProvider, useToggle, } from '@openmsupply-client/common'; import { DraftRequestLine } from './hooks'; @@ -71,6 +72,7 @@ export const RequestLineEdit = ({ }: RequestLineEditProps) => { const t = useTranslation(); const navigate = useNavigate(); + const { plugins } = usePluginProvider(); const { isOn, toggle } = useToggle(); const [anchorEl, setAnchorEl] = React.useState(null); const { store } = useAuthContext(); @@ -83,6 +85,9 @@ export const RequestLineEdit = ({ ?.sort((a, b) => a.name.name.localeCompare(b.name.name)) .sort((a, b) => b.amcInUnits - a.amcInUnits) .sort((a, b) => b.stockInUnits - a.stockInUnits); + + const line = lines.find(line => line.id === draft?.id); + return ( @@ -213,6 +218,10 @@ export const RequestLineEdit = ({ label={t('label.amc')} sx={{ marginBottom: 1 }} /> + {line && + plugins.requestRequisitionColumn?.editViewColumns?.map( + (Column, index) => + )} {isProgram && useConsumptionData && ( { const columns = useColumns( [ ...columnDefinitions, - ...(plugins.requestRequisitionColumn?.columns || []), + ...(plugins.requestRequisitionColumn?.tableColumns || []), ], { onChangeSortBy: updateSortQuery, From 57a6a5f8d720524255c2c13967fc69dcae4340b8 Mon Sep 17 00:00:00 2001 From: Andrei Date: Wed, 26 Feb 2025 00:56:31 +1300 Subject: [PATCH 3/4] Revert central server hack --- server/service/src/sync/mod.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/server/service/src/sync/mod.rs b/server/service/src/sync/mod.rs index f4e88d07d0..8c8aa8adbb 100644 --- a/server/service/src/sync/mod.rs +++ b/server/service/src/sync/mod.rs @@ -118,11 +118,10 @@ impl CentralServerConfig { } pub fn is_central_server() -> bool { - true - // CENTRAL_SERVER_CONFIG - // .read() - // .unwrap() - // .inner_is_central_server() + CENTRAL_SERVER_CONFIG + .read() + .unwrap() + .inner_is_central_server() } pub fn get() -> Self { From 50594865584a3c1bfbb2262ecf5f39cb0fd09b5a Mon Sep 17 00:00:00 2001 From: Andrei Date: Thu, 27 Feb 2025 23:10:58 +1300 Subject: [PATCH 4/4] Use ts chrono impl feature --- server/Cargo.lock | 1 + server/Cargo.toml | 2 +- .../repository/src/db_diesel/requisition/requisition_row.rs | 4 ---- .../src/db_diesel/requisition_line/requisition_line_row.rs | 1 - 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/server/Cargo.lock b/server/Cargo.lock index b9b069e321..f266cd6ca8 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -9284,6 +9284,7 @@ version = "10.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e640d9b0964e9d39df633548591090ab92f7a4567bc31d3891af23471a3365c6" dependencies = [ + "chrono", "lazy_static", "thiserror 2.0.11", "ts-rs-macros", diff --git a/server/Cargo.toml b/server/Cargo.toml index 00bd4bcaa7..5ec12b5f29 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -46,7 +46,7 @@ actix-files = "0.6.6" log-panics = { version = "2.1.0", features = ["with-backtrace"] } extism = "1.9.1" base64 = "0.22.1" -ts-rs = "10.1" +ts-rs = { version = "10.1", features = ["chrono-impl"] } # TODO: Remove. async_graphql is using 1.0 but SET_COOKIE was being imported from wrong version. http2 = { package = "http", version = "1.0.0" } diff --git a/server/repository/src/db_diesel/requisition/requisition_row.rs b/server/repository/src/db_diesel/requisition/requisition_row.rs index 85010cbdfc..d389a12b27 100644 --- a/server/repository/src/db_diesel/requisition/requisition_row.rs +++ b/server/repository/src/db_diesel/requisition/requisition_row.rs @@ -94,13 +94,9 @@ pub struct RequisitionRow { #[diesel(column_name = type_)] pub r#type: RequisitionType, pub status: RequisitionStatus, - #[ts(as = "String")] pub created_datetime: NaiveDateTime, - #[ts(as = "Option")] pub sent_datetime: Option, - #[ts(as = "Option")] pub finalised_datetime: Option, - #[ts(as = "Option")] pub expected_delivery_date: Option, pub colour: Option, pub comment: Option, diff --git a/server/repository/src/db_diesel/requisition_line/requisition_line_row.rs b/server/repository/src/db_diesel/requisition_line/requisition_line_row.rs index ce6ebd67ac..ffaab68474 100644 --- a/server/repository/src/db_diesel/requisition_line/requisition_line_row.rs +++ b/server/repository/src/db_diesel/requisition_line/requisition_line_row.rs @@ -56,7 +56,6 @@ pub struct RequisitionLineRow { pub supply_quantity: f64, pub available_stock_on_hand: f64, pub average_monthly_consumption: f64, - #[ts(as = "Option")] pub snapshot_datetime: Option, pub approved_quantity: f64, pub approval_comment: Option,