Skip to content

Commit

Permalink
NEXT-37602 - Add currency lookup
Browse files Browse the repository at this point in the history
  • Loading branch information
DennisGarding committed Aug 14, 2024
1 parent 6f7a140 commit ff251b3
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 39 deletions.
15 changes: 8 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# NEXT-RELEASE

- NEXT-37317 - Added various default profiles
- NEXT-37504 - Fixed the generation of request criteria to support correct nested associations
- NEXT-37318 - Added `copy-profile` command, to copy the default profiles to your system
- NEXT-37316 - Added `index` command, to trigger the indexing of the Shopware shop
- NEXT-37315 - Trigger indexing of the shop by default at the end of an import (can be disabled with flag `-d` `--disable-index`)
- NEXT-37303 - [BREAKING] changed `sync` command argument `--schema` `-s` to `--profile` `-p`
- NEXT-37303 - [BREAKING] Fixed an issue where `row` values were always provided as strings in the deserialize script.
- NEXT-37303 - [BREAKING] Fixed an issue where `row` values were always provided as strings in the deserialize script.
Now they are converted into their proper types before passed to the script.
- NEXT-37313 - Implemented re-authentication for API calls to handle expired bearer tokens
- NEXT-37314 - Added new scripting function `get_language_by_iso` for profiles to environment
- NEXT-37315 - Trigger indexing of the shop by default at the end of an import (can be disabled with flag `-d` `--disable-index`)
- NEXT-37316 - Added `index` command, to trigger the indexing of the Shopware shop
- NEXT-37317 - Added various default profiles
- NEXT-37318 - Added `copy-profile` command, to copy the default profiles to your system
- NEXT-37504 - Fixed the generation of request criteria to support correct nested associations
- NEXT-37602 - Added new scripting function `get_currency_by_iso` for profiles to environment

# v0.7.1

Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,18 @@ deserialize_script: |
linked: true,
currencyId: get_default("CURRENCY"),
});
// You can get the default language or a specific language id by their ISO code
// Default language is used here and will return the default language id
let default_language_id = get_default("LANGUAGE_SYSTEM");
// For a specific language id you can use the get_language_by_iso function:
let specific_language_id = get_language_by_iso("de-DE"); // It will return the language id for "de-DE"
// You can also get different currencies by their ISO code
// Default currency is used here and will return the default currency id
get_default("CURRENCY")
// For a specific currency id you can use the get_currency_by_iso function:
let eur_currency = get_currency_by_iso("EUR"); // It will return the currency id for "EUR"
```
## License
Expand Down
114 changes: 92 additions & 22 deletions src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,18 +62,8 @@ impl SwClient {

criteria.add_association("locale");

let request_builder = self
.client
.request(
Method::POST,
format!("{}/api/search/language", self.credentials.base_url),
)
.json(&criteria);

let response = self.handle_authenticated_request(request_builder)?;

let value: LanguageLocaleSearchResponse = Self::deserialize(response)?;
for item in value.data {
let list: SwListResponse<Language> = self.list("language", &criteria)?;
for item in list.data {
language_list.insert(item.locale.code, item.id);
}

Expand All @@ -85,6 +75,33 @@ impl SwClient {
})
}

pub fn get_currencies(&self) -> Result<CurrencyList, SwApiError> {
let mut page = 1;
let mut currency_list: HashMap<String, String> = HashMap::new();

let total = self.get_total("currency", &[])?;

while currency_list.len() < total as usize {
let criteria = Criteria {
page,
limit: Some(Criteria::MAX_LIMIT),
fields: vec!["id".to_string(), "isoCode".to_string()],
..Default::default()
};

let list: SwListResponse<Currency> = self.list("currency", &criteria)?;
for item in list.data {
currency_list.insert(item.iso_code, item.id);
}

page += 1;
}

Ok(CurrencyList {
data: currency_list,
})
}

pub fn sync<S: Into<String>, T: Serialize + Debug>(
&self,
entity: S,
Expand Down Expand Up @@ -190,7 +207,14 @@ impl SwClient {
Ok(count)
}

pub fn list(&self, entity: &str, criteria: &Criteria) -> Result<SwListResponse, SwApiError> {
pub fn list<T>(
&self,
entity: &str,
criteria: &Criteria,
) -> Result<SwListResponse<T>, SwApiError>
where
T: for<'a> Deserialize<'a> + Debug + Send + 'static,
{
// entity needs to be provided as kebab-case instead of snake_case
let entity = entity.replace('_', "-");

Expand Down Expand Up @@ -219,7 +243,7 @@ impl SwClient {
return Err(SwApiError::Server(status, body));
}

let value: SwListResponse = Self::deserialize(response)?;
let value: SwListResponse<T> = Self::deserialize(response)?;

Ok(value)
}
Expand Down Expand Up @@ -346,7 +370,6 @@ impl SwClient {
}
}
}

#[derive(Debug, Serialize)]
struct IndexBody {
skip: Vec<String>,
Expand Down Expand Up @@ -418,8 +441,32 @@ pub struct SwErrorSource {
}

#[derive(Debug, Deserialize)]
pub struct SwListResponse {
pub data: Vec<Entity>,
pub struct SwListResponse<T> {
pub data: Vec<T>,
}

#[derive(Debug, Deserialize)]
pub struct Currency {
pub id: String,
#[serde(rename = "isoCode")]
pub iso_code: String,
}

#[derive(Debug, Clone, Default)]
pub struct CurrencyList {
pub data: HashMap<String, String>,
}

impl CurrencyList {
pub fn get_currency_id_by_iso_code(&self, iso_code: &str) -> String {
match self.data.get(iso_code) {
Some(id) => id.to_string(),
None => {
println!("Currency with iso code '{}' not found", iso_code);
"".to_string()
}
}
}
}

#[derive(Debug, Deserialize)]
Expand All @@ -433,11 +480,6 @@ pub struct Locale {
pub code: String,
}

#[derive(Debug, Deserialize)]
pub struct LanguageLocaleSearchResponse {
pub data: Vec<Language>,
}

#[derive(Debug, Clone, Default)]
pub struct IsoLanguageList {
pub data: HashMap<String, String>,
Expand All @@ -459,6 +501,7 @@ pub type Entity = serde_json::Map<String, serde_json::Value>;

#[cfg(test)]
mod tests {
use crate::api::CurrencyList;
use crate::api::IsoLanguageList;
use std::collections::HashMap;

Expand Down Expand Up @@ -488,4 +531,31 @@ mod tests {
);
assert_eq!(locale_list.get_language_id_by_iso_code("en-US"), "");
}

#[test]
fn test_currency_list() {
let mut currency_list_inner: HashMap<String, String> = HashMap::new();
currency_list_inner.insert(
"EUR".to_string(),
"a55d590baf2c432999f650f421f25eb6".to_string(),
);
currency_list_inner.insert(
"USD".to_string(),
"cae49554610b4df2be0fbd61be51f66d".to_string(),
);

let currency_list = CurrencyList {
data: currency_list_inner,
};

assert_eq!(
currency_list.get_currency_id_by_iso_code("EUR"),
"a55d590baf2c432999f650f421f25eb6"
);
assert_eq!(
currency_list.get_currency_id_by_iso_code("USD"),
"cae49554610b4df2be0fbd61be51f66d"
);
assert_eq!(currency_list.get_currency_id_by_iso_code("GBP"), "");
}
}
3 changes: 2 additions & 1 deletion src/config_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ pub struct EntityScriptMapping {
#[cfg(test)]
mod tests {
use super::*;
use crate::api::{Entity, IsoLanguageList};
use crate::api::{CurrencyList, Entity, IsoLanguageList};
use crate::data::{prepare_scripting_environment, validate_paths_for_entity};

#[test]
Expand Down Expand Up @@ -205,6 +205,7 @@ mod tests {
&profile.serialize_script,
&profile.deserialize_script,
IsoLanguageList::default(),
CurrencyList::default(),
)
.expect(&format!(
"failed to compile scripts in default profile {profile_filename}"
Expand Down
6 changes: 3 additions & 3 deletions src/data/export.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Everything related to exporting data out of shopware
use crate::api::filter::Criteria;
use crate::api::SwListResponse;
use crate::api::{Entity, SwListResponse};
use crate::data::transform::serialize_entity;
use crate::SyncContext;
use std::cmp;
Expand Down Expand Up @@ -88,7 +88,7 @@ fn send_request(
page: u64,
chunk_limit: usize,
context: &SyncContext,
) -> anyhow::Result<SwListResponse> {
) -> anyhow::Result<SwListResponse<Entity>> {
let mut criteria = Criteria {
page,
limit: Some(chunk_limit),
Expand All @@ -109,7 +109,7 @@ fn send_request(
fn process_response(
page: u64,
chunk_limit: usize,
response: SwListResponse,
response: SwListResponse<Entity>,
context: &SyncContext,
) -> anyhow::Result<(u64, Vec<Vec<String>>)> {
let mut rows: Vec<Vec<String>> = Vec::with_capacity(chunk_limit);
Expand Down
38 changes: 33 additions & 5 deletions src/data/transform/script.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Everything scripting related
use crate::api::{Entity, IsoLanguageList};
use crate::api::{CurrencyList, Entity, IsoLanguageList};
use crate::config_file::{Mapping, Profile};
use crate::data::transform::get_json_value_from_string;
use anyhow::Context;
Expand Down Expand Up @@ -104,8 +104,9 @@ pub fn prepare_scripting_environment(
raw_serialize_script: &str,
raw_deserialize_script: &str,
language_list: IsoLanguageList,
currency_list: CurrencyList,
) -> anyhow::Result<ScriptingEnvironment> {
let engine = get_base_engine(language_list);
let engine = get_base_engine(language_list, currency_list);
let serialize_ast = if raw_serialize_script.is_empty() {
None
} else {
Expand All @@ -130,7 +131,7 @@ pub fn prepare_scripting_environment(
})
}

fn get_base_engine(language_list: IsoLanguageList) -> Engine {
fn get_base_engine(language_list: IsoLanguageList, currency_list: CurrencyList) -> Engine {
let mut engine = Engine::new_raw();
engine.set_optimization_level(OptimizationLevel::Full);

Expand All @@ -157,6 +158,10 @@ fn get_base_engine(language_list: IsoLanguageList) -> Engine {
language_list.get_language_id_by_iso_code(iso)
});

engine.register_fn("get_currency_by_iso", move |iso: &str| {
currency_list.get_currency_id_by_iso_code(iso)
});

// Some reference implementations below
/*
engine.register_type::<Uuid>();
Expand Down Expand Up @@ -239,6 +244,24 @@ mod tests {
locale_list
}

fn create_currency_list() -> CurrencyList {
let mut currency_list_inner: HashMap<String, String> = HashMap::new();
currency_list_inner.insert(
"EUR".to_string(),
"a55d590baf2c432999f650f421f25eb6".to_string(),
);
currency_list_inner.insert(
"USD".to_string(),
"cae49554610b4df2be0fbd61be51f66d".to_string(),
);

let currency_list = CurrencyList {
data: currency_list_inner,
};

currency_list
}

#[test]
fn test_basic_serialize() {
let script_env = prepare_scripting_environment(
Expand All @@ -251,6 +274,7 @@ mod tests {
// deserialize
"#,
create_language_iso_list(),
create_currency_list(),
)
.unwrap();

Expand All @@ -277,6 +301,7 @@ mod tests {
#[test]
fn test_basic_deserialize() {
let iso_list = create_language_iso_list();
let currency_list = create_currency_list();

let script_env = prepare_scripting_environment(
r#"
Expand All @@ -286,10 +311,12 @@ mod tests {
// deserialize
entity["fiz"] = row["bar_key"];
entity["number"] = row["number_plus_one"] - 1;
entity["currencyId"] = get_default("CURRENCY");
entity["defaultCurrencyId"] = get_default("CURRENCY");
entity["languageId"] = get_language_by_iso("de-DE");
entity["currencyId"] = get_currency_by_iso("USD");
"#,
iso_list.clone(),
currency_list.clone(),
)
.unwrap();

Expand Down Expand Up @@ -319,8 +346,9 @@ mod tests {
serde_json::from_value(json!({
"fiz": "buzz",
"number": 42,
"currencyId": inside_script::get_default("CURRENCY"),
"defaultCurrencyId": inside_script::get_default("CURRENCY"),
"languageId": iso_list.get_language_id_by_iso_code("de-DE"),
"currencyId": currency_list.get_currency_id_by_iso_code("USD"),
}))
.unwrap()
);
Expand Down
3 changes: 2 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,14 @@ fn create_context(

validate_paths_for_entity(entity, &profile.mappings, &api_schema?)?;

// ToDo: create lookup table for currencies?
let language_list = sw_client.get_languages()?;
let currency_list = sw_client.get_currencies()?;

let scripting_environment = prepare_scripting_environment(
&profile.serialize_script,
&profile.deserialize_script,
language_list,
currency_list,
)?;

Ok(SyncContext {
Expand Down

0 comments on commit ff251b3

Please sign in to comment.