conversions web + put table definition
This commit is contained in:
250
web/src/pages/admin/ecb/workspace_logic.rs
Normal file
250
web/src/pages/admin/ecb/workspace_logic.rs
Normal file
@@ -0,0 +1,250 @@
|
||||
//! HTTP handlers for the provider-neutral exchange-rate administration workspace.
|
||||
|
||||
use axum::{
|
||||
extract::{Query, State},
|
||||
http::{HeaderMap, StatusCode},
|
||||
response::{Html, IntoResponse, Redirect, Response},
|
||||
};
|
||||
use axum_extra::extract::Form;
|
||||
|
||||
use crate::{
|
||||
AppState,
|
||||
definitions::exchange_rates::{
|
||||
AddProfileCurrencySourceRequest, ExchangeRateDateRule, ExchangeRateSource,
|
||||
ListConversionEvidenceRequest,
|
||||
},
|
||||
services::{authenticated_request, reject_cross_site},
|
||||
};
|
||||
|
||||
use super::{
|
||||
state::LoadError,
|
||||
workspace_loader::load_workspace,
|
||||
workspace_state::{
|
||||
ConfigurationForm, EvidenceForm, EvidenceView, PreviewForm, PreviewView, WorkspaceInputs,
|
||||
WorkspaceQuery,
|
||||
},
|
||||
workspace_ui,
|
||||
};
|
||||
|
||||
pub(crate) async fn legacy_ecb() -> Redirect {
|
||||
Redirect::permanent("/admin/exchange-rates#providers")
|
||||
}
|
||||
|
||||
pub(crate) async fn page(
|
||||
State(state): State<AppState>,
|
||||
headers: HeaderMap,
|
||||
Query(query): Query<WorkspaceQuery>,
|
||||
) -> Response {
|
||||
render_loaded(
|
||||
&headers,
|
||||
load_workspace(state, &headers, WorkspaceInputs::for_profile(query.profile)).await,
|
||||
StatusCode::OK,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) async fn add_configuration(
|
||||
State(state): State<AppState>,
|
||||
headers: HeaderMap,
|
||||
Form(form): Form<ConfigurationForm>,
|
||||
) -> Response {
|
||||
if let Some(rejection) = reject_cross_site(&headers) {
|
||||
return rejection;
|
||||
}
|
||||
let mut inputs = WorkspaceInputs::for_profile(form.profile.clone());
|
||||
inputs.configuration = form.clone();
|
||||
let added_source = form.source_id.trim().to_string();
|
||||
let request = AddProfileCurrencySourceRequest {
|
||||
profile_name: form.profile,
|
||||
currency: form.currency.trim().to_ascii_uppercase(),
|
||||
source_id: form.source_id.trim().to_string(),
|
||||
make_default: form.make_default,
|
||||
};
|
||||
let Ok(request) = authenticated_request(&headers, request) else {
|
||||
return Redirect::to("/login").into_response();
|
||||
};
|
||||
let mut exchange_rates = state.exchange_rates.clone();
|
||||
match exchange_rates.add_profile_currency_source(request).await {
|
||||
Ok(currency) => {
|
||||
let currency = currency.into_inner();
|
||||
inputs.configuration = ConfigurationForm::default();
|
||||
inputs.status = Some(crate::tr!(
|
||||
crate::i18n::Locale::from_headers(&headers),
|
||||
"rates-configuration-added",
|
||||
"currency" => currency.currency,
|
||||
"source" => added_source,
|
||||
));
|
||||
render_loaded(
|
||||
&headers,
|
||||
load_workspace(state, &headers, inputs).await,
|
||||
StatusCode::OK,
|
||||
)
|
||||
}
|
||||
Err(error) => {
|
||||
inputs.error = Some(error.message().to_string());
|
||||
render_loaded(
|
||||
&headers,
|
||||
load_workspace(state, &headers, inputs).await,
|
||||
StatusCode::UNPROCESSABLE_ENTITY,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn preview(
|
||||
State(state): State<AppState>,
|
||||
headers: HeaderMap,
|
||||
Form(form): Form<PreviewForm>,
|
||||
) -> Response {
|
||||
if let Some(rejection) = reject_cross_site(&headers) {
|
||||
return rejection;
|
||||
}
|
||||
let mut inputs = WorkspaceInputs::for_profile(form.profile.clone());
|
||||
inputs.preview_form = form.clone();
|
||||
let request = match form.request() {
|
||||
Ok(request) => request,
|
||||
Err(key) => {
|
||||
inputs.error = Some(crate::tr!(crate::i18n::Locale::from_headers(&headers), key));
|
||||
return render_loaded(
|
||||
&headers,
|
||||
load_workspace(state, &headers, inputs).await,
|
||||
StatusCode::UNPROCESSABLE_ENTITY,
|
||||
);
|
||||
}
|
||||
};
|
||||
let Ok(request) = authenticated_request(&headers, request) else {
|
||||
return Redirect::to("/login").into_response();
|
||||
};
|
||||
let mut exchange_rates = state.exchange_rates.clone();
|
||||
match exchange_rates.preview_direct_conversion(request).await {
|
||||
Ok(response) => {
|
||||
let response = response.into_inner();
|
||||
let locale = crate::i18n::Locale::from_headers(&headers);
|
||||
inputs.preview = Some(PreviewView {
|
||||
applied_method: crate::tr!(locale, rate_method_key(response.applied_source)),
|
||||
applied_date_rule: crate::tr!(locale, date_rule_key(response.applied_date_rule)),
|
||||
response,
|
||||
});
|
||||
render_loaded(
|
||||
&headers,
|
||||
load_workspace(state, &headers, inputs).await,
|
||||
StatusCode::OK,
|
||||
)
|
||||
}
|
||||
Err(error) => {
|
||||
inputs.error = Some(error.message().to_string());
|
||||
render_loaded(
|
||||
&headers,
|
||||
load_workspace(state, &headers, inputs).await,
|
||||
StatusCode::UNPROCESSABLE_ENTITY,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn evidence(
|
||||
State(state): State<AppState>,
|
||||
headers: HeaderMap,
|
||||
Form(form): Form<EvidenceForm>,
|
||||
) -> Response {
|
||||
if let Some(rejection) = reject_cross_site(&headers) {
|
||||
return rejection;
|
||||
}
|
||||
let mut inputs = WorkspaceInputs::for_profile(form.profile.clone());
|
||||
inputs.evidence_form = form.clone();
|
||||
let record_id = match form.record_id.trim().parse::<i64>() {
|
||||
Ok(id) if id > 0 => id,
|
||||
_ => {
|
||||
inputs.error = Some(crate::tr!(
|
||||
crate::i18n::Locale::from_headers(&headers),
|
||||
"rates-err-record-id"
|
||||
));
|
||||
return render_loaded(
|
||||
&headers,
|
||||
load_workspace(state, &headers, inputs).await,
|
||||
StatusCode::UNPROCESSABLE_ENTITY,
|
||||
);
|
||||
}
|
||||
};
|
||||
let request = ListConversionEvidenceRequest {
|
||||
profile_name: form.profile,
|
||||
table_name: form.table_name.trim().to_string(),
|
||||
record_id,
|
||||
column_name: form.column_name.trim().to_string(),
|
||||
limit: 50,
|
||||
before_evidence_id: None,
|
||||
};
|
||||
let Ok(request) = authenticated_request(&headers, request) else {
|
||||
return Redirect::to("/login").into_response();
|
||||
};
|
||||
let mut exchange_rates = state.exchange_rates.clone();
|
||||
match exchange_rates.list_conversion_evidence(request).await {
|
||||
Ok(response) => {
|
||||
let response = response.into_inner();
|
||||
inputs.evidence_has_exchange = Some(response.has_exchange);
|
||||
inputs.evidence_has_more = response.has_more;
|
||||
inputs.evidence = response
|
||||
.evidence
|
||||
.into_iter()
|
||||
.map(|evidence| EvidenceView { evidence })
|
||||
.collect();
|
||||
render_loaded(
|
||||
&headers,
|
||||
load_workspace(state, &headers, inputs).await,
|
||||
StatusCode::OK,
|
||||
)
|
||||
}
|
||||
Err(error) => {
|
||||
inputs.error = Some(error.message().to_string());
|
||||
render_loaded(
|
||||
&headers,
|
||||
load_workspace(state, &headers, inputs).await,
|
||||
StatusCode::UNPROCESSABLE_ENTITY,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rate_method_key(value: i32) -> &'static str {
|
||||
match ExchangeRateSource::try_from(value).ok() {
|
||||
Some(ExchangeRateSource::Official) => "rates-source-official",
|
||||
Some(ExchangeRateSource::SavedCustom) => "rates-source-saved",
|
||||
Some(ExchangeRateSource::Manual) => "rates-source-manual",
|
||||
_ => "rates-unknown",
|
||||
}
|
||||
}
|
||||
|
||||
fn date_rule_key(value: i32) -> &'static str {
|
||||
match ExchangeRateDateRule::try_from(value).ok() {
|
||||
Some(ExchangeRateDateRule::PreviousPublication) => "rates-rule-previous",
|
||||
Some(ExchangeRateDateRule::OnOrBeforeDate) => "rates-rule-on-or-before",
|
||||
Some(ExchangeRateDateRule::SpecificPublicationDate) => "rates-rule-specific",
|
||||
_ => "rates-unknown",
|
||||
}
|
||||
}
|
||||
|
||||
fn render_loaded(
|
||||
headers: &HeaderMap,
|
||||
result: Result<super::workspace_state::ExchangeRateWorkspaceState, LoadError>,
|
||||
success_status: StatusCode,
|
||||
) -> Response {
|
||||
match result {
|
||||
Ok(page) => (success_status, Html(workspace_ui::render_page(&page))).into_response(),
|
||||
Err(LoadError::Unauthenticated) => Redirect::to("/login").into_response(),
|
||||
Err(LoadError::Forbidden) => (
|
||||
StatusCode::FORBIDDEN,
|
||||
Html(workspace_ui::render_error(
|
||||
crate::i18n::Locale::from_headers(headers),
|
||||
&crate::tr!(crate::i18n::Locale::from_headers(headers), "ecb-forbidden"),
|
||||
)),
|
||||
)
|
||||
.into_response(),
|
||||
Err(LoadError::Backend(message)) => (
|
||||
StatusCode::BAD_GATEWAY,
|
||||
Html(workspace_ui::render_error(
|
||||
crate::i18n::Locale::from_headers(headers),
|
||||
&message,
|
||||
)),
|
||||
)
|
||||
.into_response(),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user