features, cleanup and bug fixes #26
10 changed files with 225 additions and 214 deletions
reactor Error types
better separate response error types used for route handlers and other errors - with an exception of PresentationError
commit
d98df7839d
43
src/error.rs
43
src/error.rs
|
|
@ -42,11 +42,6 @@ impl Display for LoginRequired {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Error for LoginRequired {}
|
impl Error for LoginRequired {}
|
||||||
impl ResponseError for LoginRequired {
|
|
||||||
fn status_code(&self) -> StatusCode {
|
|
||||||
StatusCode::UNAUTHORIZED
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum MultipartFieldError {
|
pub enum MultipartFieldError {
|
||||||
|
|
@ -83,39 +78,18 @@ pub enum MultipartFieldError {
|
||||||
Runtime(#[from] tokio::task::JoinError),
|
Runtime(#[from] tokio::task::JoinError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseError for MultipartFieldError {
|
|
||||||
fn status_code(&self) -> StatusCode {
|
|
||||||
match self {
|
|
||||||
MultipartFieldError::IOError(_) | MultipartFieldError::Runtime(_) => {
|
|
||||||
StatusCode::INTERNAL_SERVER_ERROR
|
|
||||||
}
|
|
||||||
MultipartFieldError::ContentTooLarge { .. }
|
|
||||||
| MultipartFieldError::Date { .. }
|
|
||||||
| MultipartFieldError::MultipartError(_)
|
|
||||||
| MultipartFieldError::NotAFile { field: _ }
|
|
||||||
| MultipartFieldError::TooManyOccurrences { .. }
|
|
||||||
| MultipartFieldError::UTF8Error(_)
|
|
||||||
| MultipartFieldError::MissingField { field: _ }
|
|
||||||
| MultipartFieldError::InvalidAddress(_) => StatusCode::BAD_REQUEST,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub(crate) enum PresentationError {
|
pub(crate) enum PresentationError {
|
||||||
#[error("Failed to render page template: {0}")]
|
#[error("Failed to render page template: {0}")]
|
||||||
Render(#[from] RenderError),
|
Render(#[from] RenderError),
|
||||||
#[error("Failed to generate URL for route: {0}")]
|
#[error("Failed to generate URL for route: {0}")]
|
||||||
Url(#[from] UrlGenerationError),
|
Url(#[from] UrlGenerationError),
|
||||||
#[error("Couldn't generate response as login is required")]
|
|
||||||
LoginRequired(#[from] LoginRequired),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseError for PresentationError {
|
impl ResponseError for PresentationError {
|
||||||
fn status_code(&self) -> StatusCode {
|
fn status_code(&self) -> StatusCode {
|
||||||
match self {
|
match self {
|
||||||
Self::Render(_) | Self::Url(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
Self::Render(_) | Self::Url(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
Self::LoginRequired(_) => StatusCode::UNAUTHORIZED,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn error_response(&self) -> HttpResponse<BoxBody> {
|
fn error_response(&self) -> HttpResponse<BoxBody> {
|
||||||
|
|
@ -185,8 +159,8 @@ mod ldap_response_code {
|
||||||
pub const UNAVAILABLE: u32 = 52;
|
pub const UNAVAILABLE: u32 = 52;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseError for AuthenticationError {
|
impl AuthenticationError {
|
||||||
fn status_code(&self) -> StatusCode {
|
pub(crate) fn as_status_code(&self) -> StatusCode {
|
||||||
match self {
|
match self {
|
||||||
AuthenticationError::Ldap(ldap_error) => match ldap_error {
|
AuthenticationError::Ldap(ldap_error) => match ldap_error {
|
||||||
LdapError::LdapResult { result } => match result.rc {
|
LdapError::LdapResult { result } => match result.rc {
|
||||||
|
|
@ -203,3 +177,16 @@ impl ResponseError for AuthenticationError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
#[error("Authentication failed: {0}")]
|
||||||
|
pub(crate) struct LoginResponseError(#[from] AuthenticationError);
|
||||||
|
|
||||||
|
impl ResponseError for LoginResponseError {
|
||||||
|
fn status_code(&self) -> StatusCode {
|
||||||
|
self.0.as_status_code()
|
||||||
|
}
|
||||||
|
fn error_response(&self) -> HttpResponse<BoxBody> {
|
||||||
|
default_error_response(self, self.status_code())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use std::time::Duration;
|
||||||
use actix_web::error::UrlGenerationError;
|
use actix_web::error::UrlGenerationError;
|
||||||
use actix_web::{HttpRequest, Result};
|
use actix_web::{HttpRequest, Result};
|
||||||
use chrono::{FixedOffset, TimeZone, Timelike};
|
use chrono::{FixedOffset, TimeZone, Timelike};
|
||||||
use error::{DeleteError, SaveError};
|
use error::DeleteError;
|
||||||
use lettre::Address;
|
use lettre::Address;
|
||||||
use log::{debug, error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
use rand::distributions::DistString;
|
use rand::distributions::DistString;
|
||||||
|
|
@ -22,6 +22,7 @@ use toml::value::Datetime;
|
||||||
|
|
||||||
use crate::auth::User;
|
use crate::auth::User;
|
||||||
use crate::error::PresentationError;
|
use crate::error::PresentationError;
|
||||||
|
use crate::job_offers::error::SaveError;
|
||||||
use crate::route::{JOBOFFER_ATTACHMENT_ROUTE, PREVIEW_ATTACHMENT_ROUTE};
|
use crate::route::{JOBOFFER_ATTACHMENT_ROUTE, PREVIEW_ATTACHMENT_ROUTE};
|
||||||
use crate::route::{JOBOFFER_DELETION_ROUTE, JOBOFFER_PUBLISH_ROUTE, JOBOFFER_UNPUBLISH_ROUTE};
|
use crate::route::{JOBOFFER_DELETION_ROUTE, JOBOFFER_PUBLISH_ROUTE, JOBOFFER_UNPUBLISH_ROUTE};
|
||||||
use crate::util::{toml_date_to_chrono_date, toml_datetime_to_chrono_datetime};
|
use crate::util::{toml_date_to_chrono_date, toml_datetime_to_chrono_datetime};
|
||||||
|
|
|
||||||
|
|
@ -6,26 +6,20 @@ use http::StatusCode;
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub(crate) enum SaveError {
|
pub(crate) enum SaveError {
|
||||||
#[error("Creating a new Job Offer failed as the generated ID is already taken")]
|
|
||||||
AlreadyExists,
|
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
IO(#[from] std::io::Error),
|
IO(#[from] std::io::Error),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
Persist(#[from] tempfile::PersistError),
|
Persist(#[from] tempfile::PersistError),
|
||||||
#[error("Could not serialize Job Offer: {0}")]
|
#[error("Could not serialize Job Offer: {0}")]
|
||||||
Serialize(#[from] toml::ser::Error),
|
Serialize(#[from] toml::ser::Error),
|
||||||
#[error("Could not generate url for confirmation link: {0}")]
|
#[error("The Runtime encountered an error!: {0}")]
|
||||||
Url(#[from] UrlGenerationError),
|
|
||||||
#[error("Could not send Confirmation E-Mail: {0}")]
|
|
||||||
Email(#[from] EmailError),
|
|
||||||
#[error("The Runtime encountered an error!")]
|
|
||||||
Runtime(#[from] tokio::task::JoinError),
|
Runtime(#[from] tokio::task::JoinError),
|
||||||
#[error("A reviewer-only setting was specified, but no valid session was found.")]
|
#[error("Creating a new Job Offer failed as the generated ID is already taken")]
|
||||||
LoginRequired(#[from] LoginRequired),
|
AlreadyExists,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseError for SaveError {
|
impl SaveError {
|
||||||
fn status_code(&self) -> StatusCode {
|
pub(crate) fn as_status_code(&self) -> StatusCode {
|
||||||
match self {
|
match self {
|
||||||
// add special handling of ErrorKind::StorageFull | FilesystemQuotaExceeded once those error kinds are stabilized
|
// add special handling of ErrorKind::StorageFull | FilesystemQuotaExceeded once those error kinds are stabilized
|
||||||
/*
|
/*
|
||||||
|
|
@ -42,14 +36,35 @@ impl ResponseError for SaveError {
|
||||||
{
|
{
|
||||||
StatusCode::INSUFFICIENT_STORAGE
|
StatusCode::INSUFFICIENT_STORAGE
|
||||||
}*/
|
}*/
|
||||||
SaveError::Persist(_)
|
SaveError::IO(_)
|
||||||
| SaveError::IO(_)
|
| SaveError::Persist(_)
|
||||||
| SaveError::Serialize(_)
|
| SaveError::Serialize(_)
|
||||||
| SaveError::Url(_)
|
|
||||||
| SaveError::Runtime(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
| SaveError::Runtime(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
SaveError::AlreadyExists => StatusCode::CONFLICT,
|
SaveError::AlreadyExists => StatusCode::CONFLICT,
|
||||||
SaveError::Email(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
}
|
||||||
SaveError::LoginRequired(_) => StatusCode::UNAUTHORIZED,
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub(crate) enum SaveResponseError {
|
||||||
|
#[error("Could not generate url for confirmation link: {0}")]
|
||||||
|
Url(#[from] UrlGenerationError),
|
||||||
|
#[error("Could not save job offer: {0}")]
|
||||||
|
Save(#[from] SaveError),
|
||||||
|
#[error("Could not send Confirmation E-Mail: {0}")]
|
||||||
|
Email(#[from] EmailError),
|
||||||
|
#[error("A reviewer-only setting was specified, but no valid session was found.")]
|
||||||
|
Login(#[from] LoginRequired),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResponseError for SaveResponseError {
|
||||||
|
fn status_code(&self) -> StatusCode {
|
||||||
|
match self {
|
||||||
|
SaveResponseError::Save(inner) => inner.as_status_code(),
|
||||||
|
SaveResponseError::Url(_) | SaveResponseError::Email(_) => {
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR
|
||||||
|
}
|
||||||
|
SaveResponseError::Login(_) => StatusCode::UNAUTHORIZED,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn error_response(&self) -> HttpResponse<BoxBody> {
|
fn error_response(&self) -> HttpResponse<BoxBody> {
|
||||||
|
|
@ -72,17 +87,3 @@ pub(crate) enum DeleteError {
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
IO(#[from] std::io::Error),
|
IO(#[from] std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseError for DeleteError {
|
|
||||||
fn status_code(&self) -> StatusCode {
|
|
||||||
match self {
|
|
||||||
DeleteError::IO(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn error_response(&self) -> HttpResponse<BoxBody> {
|
|
||||||
let status_code = self.status_code();
|
|
||||||
match self {
|
|
||||||
DeleteError::IO(inner) => default_error_response(inner, status_code),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use log::{error, trace};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
use crate::error::{AuthenticationError, PresentationError};
|
use crate::error::{LoginResponseError, PresentationError};
|
||||||
use crate::route::HTML_CONTENT;
|
use crate::route::HTML_CONTENT;
|
||||||
use crate::{template, ServerConfig};
|
use crate::{template, ServerConfig};
|
||||||
|
|
||||||
|
|
@ -73,7 +73,7 @@ pub(crate) async fn login_post(
|
||||||
query: web::Query<LoginQuery>,
|
query: web::Query<LoginQuery>,
|
||||||
config: web::Data<ServerConfig>,
|
config: web::Data<ServerConfig>,
|
||||||
session: Session,
|
session: Session,
|
||||||
) -> Result<HttpResponse, AuthenticationError> {
|
) -> Result<HttpResponse, LoginResponseError> {
|
||||||
let result = crate::auth::User::login(&form.username, &form.password, &session, &config).await;
|
let result = crate::auth::User::login(&form.username, &form.password, &session, &config).await;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
|
|
@ -99,7 +99,7 @@ pub(crate) async fn login_post(
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("failed to perform authentication for login: {}", err);
|
error!("failed to perform authentication for login: {}", err);
|
||||||
Err(err)
|
Err(err.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
use crate::auth::User;
|
use crate::auth::User;
|
||||||
use crate::error::{AuthenticationError, LoginRequired, MultipartFieldError, PresentationError};
|
use crate::error::{MultipartFieldError, PresentationError};
|
||||||
use crate::job_offers::error::{DeleteError, SaveError};
|
use crate::job_offers::error::SaveResponseError;
|
||||||
use crate::route::LOGIN_ROUTE;
|
use crate::route::LOGIN_ROUTE;
|
||||||
use crate::{route, ServerConfig};
|
use crate::{route, ServerConfig};
|
||||||
|
|
||||||
use crate::route::job_offer::error::{ConfirmationError, DeletionError, SubmissionError};
|
use crate::route::job_offer::error::{
|
||||||
|
ConfirmationResponseError, DeletionResponseError, SubmissionResponseError, SyncResponseError,
|
||||||
|
};
|
||||||
use actix_session::SessionExt;
|
use actix_session::SessionExt;
|
||||||
use actix_web::dev::ServiceResponse;
|
use actix_web::dev::ServiceResponse;
|
||||||
use actix_web::error::UrlGenerationError;
|
use actix_web::error::UrlGenerationError;
|
||||||
|
|
@ -22,9 +24,9 @@ use url::Url;
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
#[error("Some error occurred while attempting to display an error page")]
|
#[error("Some error occurred while attempting to display an error page")]
|
||||||
pub struct ErrorHandlerError;
|
pub struct ErrorHandlerResponseError;
|
||||||
|
|
||||||
impl ResponseError for ErrorHandlerError {}
|
impl ResponseError for ErrorHandlerResponseError {}
|
||||||
|
|
||||||
pub(crate) fn generic_server_error_handler<B>(
|
pub(crate) fn generic_server_error_handler<B>(
|
||||||
res: ServiceResponse,
|
res: ServiceResponse,
|
||||||
|
|
@ -32,8 +34,8 @@ pub(crate) fn generic_server_error_handler<B>(
|
||||||
title: &str,
|
title: &str,
|
||||||
msg: Option<&str>,
|
msg: Option<&str>,
|
||||||
) -> Result<ErrorHandlerResponse<B>, actix_web::Error> {
|
) -> Result<ErrorHandlerResponse<B>, actix_web::Error> {
|
||||||
let hb: &Data<Handlebars> = res.request().app_data().ok_or(ErrorHandlerError)?;
|
let hb: &Data<Handlebars> = res.request().app_data().ok_or(ErrorHandlerResponseError)?;
|
||||||
let config: &Data<ServerConfig> = res.request().app_data().ok_or(ErrorHandlerError)?;
|
let config: &Data<ServerConfig> = res.request().app_data().ok_or(ErrorHandlerResponseError)?;
|
||||||
|
|
||||||
let base = route::base(res.request(), config, title)?;
|
let base = route::base(res.request(), config, title)?;
|
||||||
let session = res.get_session();
|
let session = res.get_session();
|
||||||
|
|
@ -61,19 +63,15 @@ pub(crate) fn internal_server_error_handler<B>(
|
||||||
res: ServiceResponse,
|
res: ServiceResponse,
|
||||||
) -> Result<ErrorHandlerResponse<B>, actix_web::Error> {
|
) -> Result<ErrorHandlerResponse<B>, actix_web::Error> {
|
||||||
if let Some(err) = res.response().error() {
|
if let Some(err) = res.response().error() {
|
||||||
if let Some(err) = err.as_error::<SaveError>() {
|
if let Some(err) = err.as_error::<SaveResponseError>() {
|
||||||
error!("Internal Server Error due to SaveError: {}", err)
|
error!("Internal Server Error due to SaveError: {}", err)
|
||||||
} else if let Some(err) = err.as_error::<DeleteError>() {
|
} else if let Some(err) = err.as_error::<DeletionResponseError>() {
|
||||||
error!("Internal Server Error due to DeleteError: {}", err)
|
error!("Internal Server Error due to DeleteError: {}", err)
|
||||||
} else if let Some(err) = err.as_error::<ErrorHandlerError>() {
|
} else if let Some(err) = err.as_error::<ErrorHandlerResponseError>() {
|
||||||
error!("Internal Server Error due to ErrorHandlerError: {}", err)
|
error!("Internal Server Error due to ErrorHandlerError: {}", err)
|
||||||
} else if let Some(err) = err.as_error::<MultipartFieldError>() {
|
|
||||||
error!("Internal Server Error due to MultipartFieldError: {}", err)
|
|
||||||
} else if let Some(err) = err.as_error::<PresentationError>() {
|
} else if let Some(err) = err.as_error::<PresentationError>() {
|
||||||
error!("Internal Server Error due to PresentationError: {}", err)
|
error!("Internal Server Error due to PresentationError: {}", err)
|
||||||
} else if let Some(err) = err.as_error::<AuthenticationError>() {
|
} else if let Some(err) = err.as_error::<SubmissionResponseError>() {
|
||||||
error!("Internal Server Error due to AuthenticationError: {}", err)
|
|
||||||
} else if let Some(err) = err.as_error::<SubmissionError>() {
|
|
||||||
error!("Internal Server Error due to SubmissionError: {}", err)
|
error!("Internal Server Error due to SubmissionError: {}", err)
|
||||||
} else {
|
} else {
|
||||||
error!("Unknown Error Type for Internal Server Error")
|
error!("Unknown Error Type for Internal Server Error")
|
||||||
|
|
@ -100,15 +98,15 @@ pub(crate) fn bad_request<B>(
|
||||||
) -> Result<ErrorHandlerResponse<B>, actix_web::Error> {
|
) -> Result<ErrorHandlerResponse<B>, actix_web::Error> {
|
||||||
let msg;
|
let msg;
|
||||||
let msg = if let Some(err) = res.response().error() {
|
let msg = if let Some(err) = res.response().error() {
|
||||||
if let Some(err) = err.as_error::<SubmissionError>() {
|
if let Some(err) = err.as_error::<SubmissionResponseError>() {
|
||||||
match err {
|
match err {
|
||||||
SubmissionError::MissingLinkOrAttachment => {
|
SubmissionResponseError::MissingLinkOrAttachment => {
|
||||||
Some("Es muss mindestens ein Link oder ein Anhang angegeben werden.")
|
Some("Es muss mindestens ein Link oder ein Anhang angegeben werden.")
|
||||||
}
|
}
|
||||||
SubmissionError::Form(MultipartFieldError::Date { err:_ }) => {
|
SubmissionResponseError::Form(MultipartFieldError::Date { err:_ }) => {
|
||||||
Some("Eine Datumsangabe entsprach nicht dem erwarteten Format.")
|
Some("Eine Datumsangabe entsprach nicht dem erwarteten Format.")
|
||||||
}
|
}
|
||||||
SubmissionError::Form(MultipartFieldError::ContentTooLarge {
|
SubmissionResponseError::Form(MultipartFieldError::ContentTooLarge {
|
||||||
field,
|
field,
|
||||||
max_byte_size,
|
max_byte_size,
|
||||||
}) => {
|
}) => {
|
||||||
|
|
@ -117,32 +115,32 @@ pub(crate) fn bad_request<B>(
|
||||||
);
|
);
|
||||||
Some(msg.as_str())
|
Some(msg.as_str())
|
||||||
}
|
}
|
||||||
SubmissionError::Form(MultipartFieldError::TooManyOccurrences { field, limit }) => {
|
SubmissionResponseError::Form(MultipartFieldError::TooManyOccurrences { field, limit }) => {
|
||||||
msg = format!(
|
msg = format!(
|
||||||
"Das Feld mit der ID {field} wurde zu oft vorhanden, erlaubt sind {limit} vorkommen."
|
"Das Feld mit der ID {field} wurde zu oft vorhanden, erlaubt sind {limit} vorkommen."
|
||||||
);
|
);
|
||||||
Some(msg.as_str())
|
Some(msg.as_str())
|
||||||
}
|
}
|
||||||
SubmissionError::Form(MultipartFieldError::MultipartError(mpe)) => {
|
SubmissionResponseError::Form(MultipartFieldError::MultipartError(mpe)) => {
|
||||||
warn!("{}", mpe);
|
warn!("{}", mpe);
|
||||||
msg = format!("{}", mpe);
|
msg = format!("{}", mpe);
|
||||||
Some(msg.as_str())
|
Some(msg.as_str())
|
||||||
}
|
}
|
||||||
SubmissionError::Form(MultipartFieldError::NotAFile { field }) => {
|
SubmissionResponseError::Form(MultipartFieldError::NotAFile { field }) => {
|
||||||
msg = format!(
|
msg = format!(
|
||||||
"Das Feld mit der ID {field} erwartet eine Datei, aber der zugehörige ContentDisposition-Header enthielt keinen Dateinamen."
|
"Das Feld mit der ID {field} erwartet eine Datei, aber der zugehörige ContentDisposition-Header enthielt keinen Dateinamen."
|
||||||
);
|
);
|
||||||
Some(msg.as_str())
|
Some(msg.as_str())
|
||||||
}
|
}
|
||||||
SubmissionError::Form(MultipartFieldError::MissingField { field }) => {
|
SubmissionResponseError::Form(MultipartFieldError::MissingField { field }) => {
|
||||||
msg =
|
msg =
|
||||||
format!("Das Feld mit der ID {field} fehlt obwohl es nicht optional ist.");
|
format!("Das Feld mit der ID {field} fehlt obwohl es nicht optional ist.");
|
||||||
Some(msg.as_str())
|
Some(msg.as_str())
|
||||||
}
|
}
|
||||||
SubmissionError::Form(MultipartFieldError::UTF8Error(_err)) => {
|
SubmissionResponseError::Form(MultipartFieldError::UTF8Error(_err)) => {
|
||||||
Some("Ein Feld das im UTF-8 format erwartet wurde konnte nicht als UTF-8 geparst werden.")
|
Some("Ein Feld das im UTF-8 format erwartet wurde konnte nicht als UTF-8 geparst werden.")
|
||||||
}
|
}
|
||||||
SubmissionError::Form(MultipartFieldError::InvalidAddress(reason)) => {
|
SubmissionResponseError::Form(MultipartFieldError::InvalidAddress(reason)) => {
|
||||||
Some(match reason {
|
Some(match reason {
|
||||||
AddressError::MissingParts => { "Unvollständige E-Mail Address" }
|
AddressError::MissingParts => { "Unvollständige E-Mail Address" }
|
||||||
AddressError::Unbalanced => { "Unausgeglichene Klammern '<' & '>' in E-Mail Address. " }
|
AddressError::Unbalanced => { "Unausgeglichene Klammern '<' & '>' in E-Mail Address. " }
|
||||||
|
|
@ -150,11 +148,11 @@ pub(crate) fn bad_request<B>(
|
||||||
AddressError::InvalidDomain => { "Der Host/Domain Teil (nach dem @) der E-Mail Address ist ungültig." }
|
AddressError::InvalidDomain => { "Der Host/Domain Teil (nach dem @) der E-Mail Address ist ungültig." }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
SubmissionError::Save(_)
|
SubmissionResponseError::Save(_)
|
||||||
| SubmissionError::TooManyRequests
|
| SubmissionResponseError::TooManyRequests
|
||||||
| SubmissionError::Render(_)
|
| SubmissionResponseError::Render(_)
|
||||||
| SubmissionError::Form(MultipartFieldError::IOError(_))
|
| SubmissionResponseError::Form(MultipartFieldError::IOError(_))
|
||||||
| SubmissionError::Form(MultipartFieldError::Runtime(_)) => {
|
| SubmissionResponseError::Form(MultipartFieldError::Runtime(_)) => {
|
||||||
error!(
|
error!(
|
||||||
"Response Status Code (Bad Request) and Error appear to disagree : {}",
|
"Response Status Code (Bad Request) and Error appear to disagree : {}",
|
||||||
err
|
err
|
||||||
|
|
@ -162,17 +160,17 @@ pub(crate) fn bad_request<B>(
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(err) = err.as_error::<ConfirmationError>() {
|
} else if let Some(err) = err.as_error::<ConfirmationResponseError>() {
|
||||||
match err {
|
match err {
|
||||||
ConfirmationError::InvalidRequest => {
|
ConfirmationResponseError::InvalidRequest => {
|
||||||
Some("Die Stellenanzeige erwartet keine Bestätigung, die Stellenanzeigen ID ist ungültig oder der Bestätigungstoken is ungültig.")
|
Some("Die Stellenanzeige erwartet keine Bestätigung, die Stellenanzeigen ID ist ungültig oder der Bestätigungstoken is ungültig.")
|
||||||
}
|
}
|
||||||
ConfirmationError::Save(_)
|
ConfirmationResponseError::Save(_)
|
||||||
| ConfirmationError::Delete(_)
|
| ConfirmationResponseError::Delete(_)
|
||||||
| ConfirmationError::SuccessRenderError(_)
|
| ConfirmationResponseError::SuccessRenderError(_)
|
||||||
| ConfirmationError::RenderError(_)
|
| ConfirmationResponseError::RenderError(_)
|
||||||
| ConfirmationError::Url(_)
|
| ConfirmationResponseError::Url(_)
|
||||||
| ConfirmationError::Presentation(_) => {
|
| ConfirmationResponseError::Presentation(_) => {
|
||||||
error!(
|
error!(
|
||||||
"Response Status Code (Bad Request) and Error appear to disagree : {}",
|
"Response Status Code (Bad Request) and Error appear to disagree : {}",
|
||||||
err
|
err
|
||||||
|
|
@ -180,17 +178,17 @@ pub(crate) fn bad_request<B>(
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(err) = err.as_error::<MultipartFieldError>() {
|
} else if let Some(err) = err.as_error::<DeletionResponseError>() {
|
||||||
warn!("Unexpected MultipartFieldError as top level error!");
|
|
||||||
msg = err.to_string();
|
|
||||||
Some(msg.as_str())
|
|
||||||
} else if let Some(err) = err.as_error::<DeletionError>() {
|
|
||||||
match err {
|
match err {
|
||||||
DeletionError::Delete(err) => {
|
DeletionResponseError::Presentation(err) => {
|
||||||
|
warn!("Failed to present deletion response: {}", err);
|
||||||
|
Some("Failed to generate response.")
|
||||||
|
}
|
||||||
|
DeletionResponseError::Delete(err) => {
|
||||||
warn!("Couldn't delete Job Offer: {}", err);
|
warn!("Couldn't delete Job Offer: {}", err);
|
||||||
Some("Could not delete a Job Offer")
|
Some("Could not delete a Job Offer")
|
||||||
}
|
}
|
||||||
DeletionError::Login(_) => {
|
DeletionResponseError::Login(_) => {
|
||||||
error!(
|
error!(
|
||||||
"Response Status Code (Bad Request) and Error appear to disagree : {}",
|
"Response Status Code (Bad Request) and Error appear to disagree : {}",
|
||||||
err
|
err
|
||||||
|
|
@ -226,23 +224,22 @@ pub(crate) fn unauthorized_error_handler<B>(
|
||||||
.response()
|
.response()
|
||||||
.error()
|
.error()
|
||||||
.and_then(|err| {
|
.and_then(|err| {
|
||||||
err.as_error::<LoginRequired>()
|
err.as_error::<SaveResponseError>()
|
||||||
.or_else(|| {
|
.and_then(|elem| match elem {
|
||||||
err.as_error::<SaveError>().and_then(|elem| match elem {
|
SaveResponseError::Login(login) => Some(login),
|
||||||
SaveError::LoginRequired(login) => Some(login),
|
_ => None,
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
err.as_error::<DeletionError>().and_then(|elem| match elem {
|
err.as_error::<DeletionResponseError>()
|
||||||
DeletionError::Login(login) => Some(login),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.or_else(|| {
|
|
||||||
err.as_error::<PresentationError>()
|
|
||||||
.and_then(|elem| match elem {
|
.and_then(|elem| match elem {
|
||||||
PresentationError::LoginRequired(login) => Some(login),
|
DeletionResponseError::Login(login) => Some(login),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
err.as_error::<SyncResponseError>()
|
||||||
|
.and_then(|elem| match elem {
|
||||||
|
SyncResponseError::Login(login) => Some(login),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use actix_session::Session;
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
get, http::header, post, web, web::ServiceConfig, HttpRequest, HttpResponse, Responder, Result,
|
get, http::header, post, web, web::ServiceConfig, HttpRequest, HttpResponse, Responder, Result,
|
||||||
};
|
};
|
||||||
use error::AttachmentError;
|
use error::AttachmentResponseError;
|
||||||
use handlebars::Handlebars;
|
use handlebars::Handlebars;
|
||||||
use http::header::CONTENT_TYPE;
|
use http::header::CONTENT_TYPE;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
@ -17,7 +17,7 @@ pub(crate) mod error;
|
||||||
use crate::auth::User;
|
use crate::auth::User;
|
||||||
use crate::error::PresentationError;
|
use crate::error::PresentationError;
|
||||||
use crate::job_offers::JobOffers;
|
use crate::job_offers::JobOffers;
|
||||||
use crate::route::job_offer::error::SyncError;
|
use crate::route::job_offer::error::SyncResponseError;
|
||||||
use crate::route::{HTML_CONTENT, JSON_CONTENT};
|
use crate::route::{HTML_CONTENT, JSON_CONTENT};
|
||||||
use crate::server_config::ServerConfig;
|
use crate::server_config::ServerConfig;
|
||||||
use crate::template;
|
use crate::template;
|
||||||
|
|
@ -93,14 +93,14 @@ pub(crate) async fn job_offer_attachment(
|
||||||
config: web::Data<ServerConfig>,
|
config: web::Data<ServerConfig>,
|
||||||
session: Session,
|
session: Session,
|
||||||
offers: web::Data<JobOffers>,
|
offers: web::Data<JobOffers>,
|
||||||
) -> Result<impl Responder, AttachmentError> {
|
) -> Result<impl Responder, AttachmentResponseError> {
|
||||||
let id = &path.id;
|
let id = &path.id;
|
||||||
let attachment_name = path.attachment.as_str();
|
let attachment_name = path.attachment.as_str();
|
||||||
|
|
||||||
let offer = offers
|
let offer = offers
|
||||||
.get_offer(id)
|
.get_offer(id)
|
||||||
.await
|
.await
|
||||||
.ok_or(AttachmentError::OfferNotFound)?;
|
.ok_or(AttachmentResponseError::OfferNotFound)?;
|
||||||
|
|
||||||
if !offer.is_published()
|
if !offer.is_published()
|
||||||
&& !query
|
&& !query
|
||||||
|
|
@ -174,7 +174,7 @@ pub(crate) async fn sync(
|
||||||
session: Session,
|
session: Session,
|
||||||
config: web::Data<ServerConfig>,
|
config: web::Data<ServerConfig>,
|
||||||
offers: web::Data<JobOffers>,
|
offers: web::Data<JobOffers>,
|
||||||
) -> Result<HttpResponse, SyncError> {
|
) -> Result<HttpResponse, SyncResponseError> {
|
||||||
// TODO return the user to a page where they are asked to confirm syncing
|
// TODO return the user to a page where they are asked to confirm syncing
|
||||||
// aka. the get variant of this route
|
// aka. the get variant of this route
|
||||||
User::current(&session)?;
|
User::current(&session)?;
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use serde_json::json;
|
||||||
|
|
||||||
use crate::auth::User;
|
use crate::auth::User;
|
||||||
use crate::error::PresentationError;
|
use crate::error::PresentationError;
|
||||||
use crate::route::job_offer::error::{DeletionError, StateChangeError};
|
use crate::route::job_offer::error::{DeletionResponseError, StateChangeResponseError};
|
||||||
use crate::route::{HTML_CONTENT, JOBOFFER_OVERVIEW_ROUTE};
|
use crate::route::{HTML_CONTENT, JOBOFFER_OVERVIEW_ROUTE};
|
||||||
use crate::{auth, template, JobOffers, ServerConfig};
|
use crate::{auth, template, JobOffers, ServerConfig};
|
||||||
|
|
||||||
|
|
@ -20,7 +20,7 @@ pub(crate) async fn delete_joboffer(
|
||||||
offers: web::Data<JobOffers>,
|
offers: web::Data<JobOffers>,
|
||||||
config: web::Data<ServerConfig>,
|
config: web::Data<ServerConfig>,
|
||||||
session: Session,
|
session: Session,
|
||||||
) -> actix_web::Result<HttpResponse, DeletionError> {
|
) -> actix_web::Result<HttpResponse, DeletionResponseError> {
|
||||||
// TODO return the user to a page where they are asked to confirm deletion
|
// TODO return the user to a page where they are asked to confirm deletion
|
||||||
// aka. the get variant of this route
|
// aka. the get variant of this route
|
||||||
let _user = auth::User::current(&session)?;
|
let _user = auth::User::current(&session)?;
|
||||||
|
|
@ -47,7 +47,7 @@ pub(crate) async fn delete_expired_joboffers(
|
||||||
offers: web::Data<JobOffers>,
|
offers: web::Data<JobOffers>,
|
||||||
config: web::Data<ServerConfig>,
|
config: web::Data<ServerConfig>,
|
||||||
session: Session,
|
session: Session,
|
||||||
) -> actix_web::Result<HttpResponse, PresentationError> {
|
) -> actix_web::Result<HttpResponse, DeletionResponseError> {
|
||||||
let user = User::current(&session)?;
|
let user = User::current(&session)?;
|
||||||
let offers_guard = offers.get_offers().await;
|
let offers_guard = offers.get_offers().await;
|
||||||
|
|
||||||
|
|
@ -57,14 +57,17 @@ pub(crate) async fn delete_expired_joboffers(
|
||||||
Some(&user),
|
Some(&user),
|
||||||
);
|
);
|
||||||
|
|
||||||
let base = crate::route::base(&req, &config, "Delete Expired")?;
|
let base =
|
||||||
|
crate::route::base(&req, &config, "Delete Expired").map_err(PresentationError::Url)?;
|
||||||
|
|
||||||
let data = json! {{
|
let data = json! {{
|
||||||
"base": base,
|
"base": base,
|
||||||
"expired_job_offers": job_offers,
|
"expired_job_offers": job_offers,
|
||||||
}};
|
}};
|
||||||
|
|
||||||
let rendered = hb.render(template::JOBOFFER_DELETE_EXPIRED, &data)?;
|
let rendered = hb
|
||||||
|
.render(template::JOBOFFER_DELETE_EXPIRED, &data)
|
||||||
|
.map_err(PresentationError::Render)?;
|
||||||
|
|
||||||
Ok(HttpResponse::Ok()
|
Ok(HttpResponse::Ok()
|
||||||
.insert_header((http::header::CONTENT_TYPE, HTML_CONTENT.clone()))
|
.insert_header((http::header::CONTENT_TYPE, HTML_CONTENT.clone()))
|
||||||
|
|
@ -127,7 +130,7 @@ pub(crate) async fn bulk_delete(
|
||||||
config: web::Data<ServerConfig>,
|
config: web::Data<ServerConfig>,
|
||||||
form: web::Form<BulkDeleteData>,
|
form: web::Form<BulkDeleteData>,
|
||||||
session: Session,
|
session: Session,
|
||||||
) -> actix_web::Result<HttpResponse, DeletionError> {
|
) -> actix_web::Result<HttpResponse, DeletionResponseError> {
|
||||||
debug!("Received bulk deletion request!");
|
debug!("Received bulk deletion request!");
|
||||||
|
|
||||||
let _user = User::current(&session)?;
|
let _user = User::current(&session)?;
|
||||||
|
|
@ -154,7 +157,7 @@ pub(crate) async fn review_joboffer(
|
||||||
session: Session,
|
session: Session,
|
||||||
config: web::Data<ServerConfig>,
|
config: web::Data<ServerConfig>,
|
||||||
offers: web::Data<JobOffers>,
|
offers: web::Data<JobOffers>,
|
||||||
) -> actix_web::Result<HttpResponse, StateChangeError> {
|
) -> actix_web::Result<HttpResponse, StateChangeResponseError> {
|
||||||
// TODO return the user to a page where they are asked to confirm publishing
|
// TODO return the user to a page where they are asked to confirm publishing
|
||||||
// aka. the get variant of this route
|
// aka. the get variant of this route
|
||||||
let _user = auth::User::current(&session)?;
|
let _user = auth::User::current(&session)?;
|
||||||
|
|
@ -182,7 +185,7 @@ pub(crate) async fn unpublish_joboffer(
|
||||||
session: Session,
|
session: Session,
|
||||||
config: web::Data<ServerConfig>,
|
config: web::Data<ServerConfig>,
|
||||||
offers: web::Data<JobOffers>,
|
offers: web::Data<JobOffers>,
|
||||||
) -> actix_web::Result<HttpResponse, StateChangeError> {
|
) -> actix_web::Result<HttpResponse, StateChangeResponseError> {
|
||||||
// TODO return the user to a page where they are asked to confirm un-publishing
|
// TODO return the user to a page where they are asked to confirm un-publishing
|
||||||
// aka. the get variant of this route
|
// aka. the get variant of this route
|
||||||
let _user = auth::User::current(&session)?;
|
let _user = auth::User::current(&session)?;
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ use serde_json::json;
|
||||||
|
|
||||||
use crate::auth::User;
|
use crate::auth::User;
|
||||||
use crate::job_offers::JobOfferData;
|
use crate::job_offers::JobOfferData;
|
||||||
use crate::route::job_offer::error::ConfirmationError;
|
use crate::route::job_offer::error::ConfirmationResponseError;
|
||||||
use crate::route::job_offer::error::ConfirmationError::SuccessRenderError;
|
use crate::route::job_offer::error::ConfirmationResponseError::SuccessRenderError;
|
||||||
use crate::route::HTML_CONTENT;
|
use crate::route::HTML_CONTENT;
|
||||||
use crate::{get, template, JobOffers, ServerConfig};
|
use crate::{get, template, JobOffers, ServerConfig};
|
||||||
|
|
||||||
|
|
@ -35,13 +35,13 @@ pub(crate) async fn confirm_joboffer_get(
|
||||||
config: web::Data<ServerConfig>,
|
config: web::Data<ServerConfig>,
|
||||||
offers: web::Data<JobOffers>,
|
offers: web::Data<JobOffers>,
|
||||||
path: web::Path<(String, String)>,
|
path: web::Path<(String, String)>,
|
||||||
) -> actix_web::Result<HttpResponse, ConfirmationError> {
|
) -> actix_web::Result<HttpResponse, ConfirmationResponseError> {
|
||||||
let id: &String = &path.0;
|
let id: &String = &path.0;
|
||||||
let req_token = &path.1;
|
let req_token = &path.1;
|
||||||
|
|
||||||
if let Some(job_offer) = offers.get_offer(id).await {
|
if let Some(job_offer) = offers.get_offer(id).await {
|
||||||
if !job_offer.check_confirmation_token(req_token) {
|
if !job_offer.check_confirmation_token(req_token) {
|
||||||
Err(ConfirmationError::InvalidRequest)
|
Err(ConfirmationResponseError::InvalidRequest)
|
||||||
} else {
|
} else {
|
||||||
let user = User::current(&session).ok();
|
let user = User::current(&session).ok();
|
||||||
|
|
||||||
|
|
@ -66,14 +66,14 @@ pub(crate) async fn confirm_joboffer_get(
|
||||||
|
|
||||||
let body = hb
|
let body = hb
|
||||||
.render(template::JOBOFFER_CONFIRM_SUBMISSION, &data)
|
.render(template::JOBOFFER_CONFIRM_SUBMISSION, &data)
|
||||||
.map_err(ConfirmationError::RenderError)?;
|
.map_err(ConfirmationResponseError::RenderError)?;
|
||||||
|
|
||||||
Ok(HttpResponse::Ok()
|
Ok(HttpResponse::Ok()
|
||||||
.insert_header((http::header::CONTENT_TYPE, HTML_CONTENT.clone()))
|
.insert_header((http::header::CONTENT_TYPE, HTML_CONTENT.clone()))
|
||||||
.body(body))
|
.body(body))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(ConfirmationError::InvalidRequest)
|
Err(ConfirmationResponseError::InvalidRequest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,7 +88,7 @@ pub(crate) async fn confirm_joboffer_post(
|
||||||
path: web::Path<(String, String)>,
|
path: web::Path<(String, String)>,
|
||||||
offers: web::Data<JobOffers>,
|
offers: web::Data<JobOffers>,
|
||||||
config: web::Data<ServerConfig>,
|
config: web::Data<ServerConfig>,
|
||||||
) -> actix_web::Result<HttpResponse, ConfirmationError> {
|
) -> actix_web::Result<HttpResponse, ConfirmationResponseError> {
|
||||||
let id = &path.0;
|
let id = &path.0;
|
||||||
let req_token = &path.1;
|
let req_token = &path.1;
|
||||||
|
|
||||||
|
|
@ -113,10 +113,10 @@ pub(crate) async fn confirm_joboffer_post(
|
||||||
.insert_header((http::header::CONTENT_TYPE, HTML_CONTENT.clone()))
|
.insert_header((http::header::CONTENT_TYPE, HTML_CONTENT.clone()))
|
||||||
.body(body))
|
.body(body))
|
||||||
}
|
}
|
||||||
Err(()) => Err(ConfirmationError::InvalidRequest),
|
Err(()) => Err(ConfirmationResponseError::InvalidRequest),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(ConfirmationError::InvalidRequest)
|
Err(ConfirmationResponseError::InvalidRequest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,7 +130,7 @@ pub(crate) async fn reject_joboffer_post(
|
||||||
hb: web::Data<Handlebars<'_>>,
|
hb: web::Data<Handlebars<'_>>,
|
||||||
offers: web::Data<JobOffers>,
|
offers: web::Data<JobOffers>,
|
||||||
config: web::Data<ServerConfig>,
|
config: web::Data<ServerConfig>,
|
||||||
) -> actix_web::Result<HttpResponse, ConfirmationError> {
|
) -> actix_web::Result<HttpResponse, ConfirmationResponseError> {
|
||||||
let id = &path.0;
|
let id = &path.0;
|
||||||
let req_token = &path.1;
|
let req_token = &path.1;
|
||||||
|
|
||||||
|
|
@ -154,9 +154,9 @@ pub(crate) async fn reject_joboffer_post(
|
||||||
.insert_header((http::header::CONTENT_TYPE, HTML_CONTENT.clone()))
|
.insert_header((http::header::CONTENT_TYPE, HTML_CONTENT.clone()))
|
||||||
.body(body))
|
.body(body))
|
||||||
} else {
|
} else {
|
||||||
Err(ConfirmationError::InvalidRequest)
|
Err(ConfirmationResponseError::InvalidRequest)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(ConfirmationError::InvalidRequest)
|
Err(ConfirmationResponseError::InvalidRequest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,14 @@ use url::Url;
|
||||||
|
|
||||||
use crate::auth::User;
|
use crate::auth::User;
|
||||||
use crate::error::{LoginRequired, MultipartFieldError, PresentationError};
|
use crate::error::{LoginRequired, MultipartFieldError, PresentationError};
|
||||||
use crate::job_offers::error::{EmailError, SaveError};
|
use crate::job_offers::error::{EmailError, SaveResponseError};
|
||||||
use crate::job_offers::{
|
use crate::job_offers::{
|
||||||
Attachment, ConfirmationStatus, JobOffer, JobOfferStatus, JobOffers, Link, MutBorrowedJobOffer,
|
Attachment, ConfirmationStatus, JobOffer, JobOfferStatus, JobOffers, Link, MutBorrowedJobOffer,
|
||||||
ReviewStatus,
|
ReviewStatus,
|
||||||
};
|
};
|
||||||
use crate::route::job_offer::confirmation::JOBOFFER_CONFIRM_ROUTE;
|
use crate::route::job_offer::confirmation::JOBOFFER_CONFIRM_ROUTE;
|
||||||
use crate::route::job_offer::create::multipart_form::{multi_field, multi_file, once_field};
|
use crate::route::job_offer::create::multipart_form::{multi_field, multi_file, once_field};
|
||||||
use crate::route::job_offer::error::SubmissionError;
|
use crate::route::job_offer::error::SubmissionResponseError;
|
||||||
use crate::route::HTML_CONTENT;
|
use crate::route::HTML_CONTENT;
|
||||||
use crate::server_config::{EmailConfig, ServerConfig};
|
use crate::server_config::{EmailConfig, ServerConfig};
|
||||||
use crate::{template, SubmissionLimiter};
|
use crate::{template, SubmissionLimiter};
|
||||||
|
|
@ -62,7 +62,7 @@ pub(crate) async fn create_joboffer_post(
|
||||||
limiter: web::Data<SubmissionLimiter>,
|
limiter: web::Data<SubmissionLimiter>,
|
||||||
hb: web::Data<Handlebars<'_>>,
|
hb: web::Data<Handlebars<'_>>,
|
||||||
multipart: Multipart,
|
multipart: Multipart,
|
||||||
) -> Result<HttpResponse, SubmissionError> {
|
) -> Result<HttpResponse, SubmissionResponseError> {
|
||||||
let user = User::current(&session).ok();
|
let user = User::current(&session).ok();
|
||||||
|
|
||||||
debug!("getting lease for new submission");
|
debug!("getting lease for new submission");
|
||||||
|
|
@ -86,7 +86,7 @@ pub(crate) async fn create_joboffer_post(
|
||||||
if submission_lease.is_none() && user.is_none() {
|
if submission_lease.is_none() && user.is_none() {
|
||||||
debug!("failed to get a lease too many requests");
|
debug!("failed to get a lease too many requests");
|
||||||
// the user is not authenticated and has reached the quota for un-authenticated users
|
// the user is not authenticated and has reached the quota for un-authenticated users
|
||||||
return Err(SubmissionError::TooManyRequests);
|
return Err(SubmissionResponseError::TooManyRequests);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("got lease, starting to process submission");
|
debug!("got lease, starting to process submission");
|
||||||
|
|
@ -112,7 +112,7 @@ pub(crate) async fn create_joboffer_post(
|
||||||
// submission failed end the lease immediately
|
// submission failed end the lease immediately
|
||||||
lease.end().await
|
lease.end().await
|
||||||
}
|
}
|
||||||
return Err(SubmissionError::MissingLinkOrAttachment);
|
return Err(SubmissionResponseError::MissingLinkOrAttachment);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("validation successful, saving submitted job offer");
|
debug!("validation successful, saving submitted job offer");
|
||||||
|
|
@ -179,7 +179,7 @@ pub(crate) async fn create_job_offer<'data, 'config>(
|
||||||
offers: &'data JobOffers,
|
offers: &'data JobOffers,
|
||||||
config: &'config ServerConfig,
|
config: &'config ServerConfig,
|
||||||
user: Option<&User>,
|
user: Option<&User>,
|
||||||
) -> Result<MutBorrowedJobOffer<'data, 'static, 'config>, SaveError> {
|
) -> Result<MutBorrowedJobOffer<'data, 'static, 'config>, SaveResponseError> {
|
||||||
let now_date = crate::util::now();
|
let now_date = crate::util::now();
|
||||||
|
|
||||||
let token: String =
|
let token: String =
|
||||||
|
|
@ -193,7 +193,7 @@ pub(crate) async fn create_job_offer<'data, 'config>(
|
||||||
{
|
{
|
||||||
// a reviewer-only option is set, but user is not logged-in as a reviewer
|
// a reviewer-only option is set, but user is not logged-in as a reviewer
|
||||||
// maybe a session just expired or some one is messing with the form
|
// maybe a session just expired or some one is messing with the form
|
||||||
return Err(SaveError::LoginRequired(LoginRequired::new()));
|
return Err(SaveResponseError::Login(LoginRequired::new()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let submission_datetime = user
|
let submission_datetime = user
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,11 @@ use log::warn;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::error::{default_error_response, LoginRequired, MultipartFieldError, PresentationError};
|
use crate::error::{default_error_response, LoginRequired, MultipartFieldError, PresentationError};
|
||||||
use crate::job_offers::error::{DeleteError, SaveError};
|
use crate::job_offers::error::{DeleteError, SaveError, SaveResponseError};
|
||||||
use crate::job_offers::JobofferLoadError;
|
use crate::job_offers::JobofferLoadError;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub(crate) enum ConfirmationError {
|
pub(crate) enum ConfirmationResponseError {
|
||||||
/// The referenced job offer does not exists,
|
/// The referenced job offer does not exists,
|
||||||
/// or does not await confirmation,
|
/// or does not await confirmation,
|
||||||
/// or the token was invalid
|
/// or the token was invalid
|
||||||
|
|
@ -33,33 +33,37 @@ pub(crate) enum ConfirmationError {
|
||||||
Presentation(#[from] PresentationError),
|
Presentation(#[from] PresentationError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseError for ConfirmationError {
|
impl ResponseError for ConfirmationResponseError {
|
||||||
fn status_code(&self) -> StatusCode {
|
fn status_code(&self) -> StatusCode {
|
||||||
match self {
|
match self {
|
||||||
ConfirmationError::RenderError(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
ConfirmationResponseError::RenderError(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
ConfirmationError::SuccessRenderError(_) => StatusCode::CREATED,
|
ConfirmationResponseError::SuccessRenderError(_) => StatusCode::CREATED,
|
||||||
ConfirmationError::InvalidRequest => StatusCode::BAD_REQUEST,
|
ConfirmationResponseError::InvalidRequest => StatusCode::BAD_REQUEST,
|
||||||
ConfirmationError::Save(inner) => inner.status_code(),
|
ConfirmationResponseError::Save(inner) => inner.as_status_code(),
|
||||||
ConfirmationError::Delete(inner) => inner.status_code(),
|
ConfirmationResponseError::Delete(DeleteError::IO(_)) => {
|
||||||
ConfirmationError::Url(inner) => inner.status_code(),
|
StatusCode::INTERNAL_SERVER_ERROR
|
||||||
ConfirmationError::Presentation(inner) => inner.status_code(),
|
}
|
||||||
|
ConfirmationResponseError::Url(inner) => inner.status_code(),
|
||||||
|
ConfirmationResponseError::Presentation(inner) => inner.status_code(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error_response(&self) -> HttpResponse<BoxBody> {
|
fn error_response(&self) -> HttpResponse<BoxBody> {
|
||||||
let status_code = self.status_code();
|
let status_code = self.status_code();
|
||||||
match self {
|
match self {
|
||||||
ConfirmationError::SuccessRenderError(inner) => {
|
ConfirmationResponseError::SuccessRenderError(inner) => {
|
||||||
warn!("Failed to render successful submission response {}", inner);
|
warn!("Failed to render successful submission response {}", inner);
|
||||||
HttpResponse::build(status_code).body("The submission was successful, but an error occurred while generating this response.")
|
HttpResponse::build(status_code).body("The submission was successful, but an error occurred while generating this response.")
|
||||||
}
|
}
|
||||||
ConfirmationError::InvalidRequest => {
|
ConfirmationResponseError::InvalidRequest => {
|
||||||
HttpResponse::build(status_code).body("Invalid Request")
|
HttpResponse::build(status_code).body("Invalid Request")
|
||||||
} // TODO more detail
|
} // TODO more detail
|
||||||
ConfirmationError::Save(inner) => inner.error_response(),
|
ConfirmationResponseError::Save(_) | ConfirmationResponseError::Delete(_) => {
|
||||||
ConfirmationError::Delete(inner) => inner.error_response(),
|
default_error_response(self, status_code)
|
||||||
ConfirmationError::Url(inner) => inner.error_response(),
|
}
|
||||||
error @ (ConfirmationError::RenderError(_) | ConfirmationError::Presentation(_)) => {
|
ConfirmationResponseError::Url(inner) => inner.error_response(),
|
||||||
|
error @ (ConfirmationResponseError::RenderError(_)
|
||||||
|
| ConfirmationResponseError::Presentation(_)) => {
|
||||||
default_error_response(error, status_code)
|
default_error_response(error, status_code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -67,30 +71,30 @@ impl ResponseError for ConfirmationError {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub(crate) enum DeletionError {
|
pub(crate) enum DeletionResponseError {
|
||||||
#[error("Could not delete Job Offer: {0}")]
|
#[error("Could not delete Job Offer: {0}")]
|
||||||
Delete(#[from] DeleteError),
|
Delete(#[from] DeleteError),
|
||||||
#[error("Login required to perform job Offer deletion: {0}")]
|
#[error("Login required to perform job Offer deletion: {0}")]
|
||||||
Login(#[from] LoginRequired),
|
Login(#[from] LoginRequired),
|
||||||
|
#[error("{0}")]
|
||||||
|
Presentation(#[from] PresentationError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseError for DeletionError {
|
impl ResponseError for DeletionResponseError {
|
||||||
fn status_code(&self) -> StatusCode {
|
fn status_code(&self) -> StatusCode {
|
||||||
match self {
|
match self {
|
||||||
DeletionError::Delete(inner) => inner.status_code(),
|
DeletionResponseError::Delete(DeleteError::IO(_))
|
||||||
DeletionError::Login(inner) => inner.status_code(),
|
| DeletionResponseError::Presentation(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
DeletionResponseError::Login(_) => StatusCode::UNAUTHORIZED,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn error_response(&self) -> HttpResponse<BoxBody> {
|
fn error_response(&self) -> HttpResponse<BoxBody> {
|
||||||
match self {
|
default_error_response(self, self.status_code())
|
||||||
DeletionError::Delete(inner) => inner.error_response(),
|
|
||||||
DeletionError::Login(inner) => inner.error_response(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub(crate) enum StateChangeError {
|
pub(crate) enum StateChangeResponseError {
|
||||||
#[error("Could not save changes to Job Offer: {0}")]
|
#[error("Could not save changes to Job Offer: {0}")]
|
||||||
Save(#[from] SaveError),
|
Save(#[from] SaveError),
|
||||||
#[error("Login required to perform job Offer deletion: {0}")]
|
#[error("Login required to perform job Offer deletion: {0}")]
|
||||||
|
|
@ -99,104 +103,120 @@ pub(crate) enum StateChangeError {
|
||||||
Presentation(#[from] PresentationError),
|
Presentation(#[from] PresentationError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RenderError> for StateChangeError {
|
impl From<RenderError> for StateChangeResponseError {
|
||||||
fn from(render: RenderError) -> Self {
|
fn from(render: RenderError) -> Self {
|
||||||
Self::Presentation(PresentationError::Render(render))
|
Self::Presentation(PresentationError::Render(render))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<UrlGenerationError> for StateChangeError {
|
impl From<UrlGenerationError> for StateChangeResponseError {
|
||||||
fn from(url_gen: UrlGenerationError) -> Self {
|
fn from(url_gen: UrlGenerationError) -> Self {
|
||||||
Self::Presentation(PresentationError::Url(url_gen))
|
Self::Presentation(PresentationError::Url(url_gen))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseError for StateChangeError {
|
impl ResponseError for StateChangeResponseError {
|
||||||
fn status_code(&self) -> StatusCode {
|
fn status_code(&self) -> StatusCode {
|
||||||
match self {
|
match self {
|
||||||
StateChangeError::Save(inner) => inner.status_code(),
|
StateChangeResponseError::Save(inner) => inner.as_status_code(),
|
||||||
StateChangeError::Login(inner) => inner.status_code(),
|
StateChangeResponseError::Login(_inner) => StatusCode::UNAUTHORIZED,
|
||||||
StateChangeError::Presentation(inner) => inner.status_code(),
|
StateChangeResponseError::Presentation(inner) => inner.status_code(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn error_response(&self) -> HttpResponse<BoxBody> {
|
fn error_response(&self) -> HttpResponse<BoxBody> {
|
||||||
match self {
|
match self {
|
||||||
StateChangeError::Save(inner) => inner.error_response(),
|
StateChangeResponseError::Login(_) | StateChangeResponseError::Save(_) => {
|
||||||
StateChangeError::Login(inner) => inner.error_response(),
|
default_error_response(self, self.status_code())
|
||||||
StateChangeError::Presentation(inner) => inner.error_response(),
|
}
|
||||||
|
StateChangeResponseError::Presentation(inner) => inner.error_response(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub(crate) enum SubmissionError {
|
pub(crate) enum SubmissionResponseError {
|
||||||
#[error("At least one link or attachment is required!")]
|
#[error("At least one link or attachment is required!")]
|
||||||
MissingLinkOrAttachment,
|
MissingLinkOrAttachment,
|
||||||
#[error("An error occurred while processing the form submission: {0}")]
|
#[error("An error occurred while processing the form submission: {0}")]
|
||||||
Form(#[from] MultipartFieldError),
|
Form(#[from] MultipartFieldError),
|
||||||
#[error("Failed to save the submission: {0}")]
|
#[error("Failed to save the submission: {0}")]
|
||||||
Save(#[from] SaveError),
|
Save(#[from] SaveResponseError),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
Render(#[from] PresentationError),
|
Render(#[from] PresentationError),
|
||||||
#[error("Too many requests!")]
|
#[error("Too many requests!")]
|
||||||
TooManyRequests,
|
TooManyRequests,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<UrlGenerationError> for SubmissionError {
|
impl From<UrlGenerationError> for SubmissionResponseError {
|
||||||
fn from(url: UrlGenerationError) -> Self {
|
fn from(url: UrlGenerationError) -> Self {
|
||||||
Self::Render(PresentationError::Url(url))
|
Self::Render(PresentationError::Url(url))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RenderError> for SubmissionError {
|
impl From<RenderError> for SubmissionResponseError {
|
||||||
fn from(render: RenderError) -> Self {
|
fn from(render: RenderError) -> Self {
|
||||||
Self::Render(PresentationError::Render(render))
|
Self::Render(PresentationError::Render(render))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseError for SubmissionError {
|
impl ResponseError for SubmissionResponseError {
|
||||||
fn status_code(&self) -> StatusCode {
|
fn status_code(&self) -> StatusCode {
|
||||||
match self {
|
match self {
|
||||||
SubmissionError::MissingLinkOrAttachment => StatusCode::BAD_REQUEST,
|
SubmissionResponseError::MissingLinkOrAttachment => StatusCode::BAD_REQUEST,
|
||||||
SubmissionError::Form(inner) => inner.status_code(),
|
SubmissionResponseError::Form(MultipartFieldError::Date { .. })
|
||||||
SubmissionError::Save(inner) => inner.status_code(),
|
| SubmissionResponseError::Form(MultipartFieldError::ContentTooLarge { .. })
|
||||||
SubmissionError::Render(inner) => inner.status_code(),
|
| SubmissionResponseError::Form(MultipartFieldError::MultipartError(_))
|
||||||
SubmissionError::TooManyRequests => StatusCode::TOO_MANY_REQUESTS,
|
| SubmissionResponseError::Form(MultipartFieldError::NotAFile { .. })
|
||||||
|
| SubmissionResponseError::Form(MultipartFieldError::TooManyOccurrences { .. })
|
||||||
|
| SubmissionResponseError::Form(MultipartFieldError::MissingField { .. })
|
||||||
|
| SubmissionResponseError::Form(MultipartFieldError::UTF8Error(_))
|
||||||
|
| SubmissionResponseError::Form(MultipartFieldError::InvalidAddress(_)) => {
|
||||||
|
StatusCode::BAD_REQUEST
|
||||||
|
}
|
||||||
|
SubmissionResponseError::Form(MultipartFieldError::IOError(_))
|
||||||
|
| SubmissionResponseError::Form(MultipartFieldError::Runtime(_)) => {
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR
|
||||||
|
}
|
||||||
|
SubmissionResponseError::Save(inner) => inner.status_code(),
|
||||||
|
SubmissionResponseError::Render(inner) => inner.status_code(),
|
||||||
|
SubmissionResponseError::TooManyRequests => StatusCode::TOO_MANY_REQUESTS,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn error_response(&self) -> HttpResponse<BoxBody> {
|
fn error_response(&self) -> HttpResponse<BoxBody> {
|
||||||
let status_code = self.status_code();
|
let status_code = self.status_code();
|
||||||
match self {
|
match self {
|
||||||
SubmissionError::MissingLinkOrAttachment => default_error_response(self, status_code),
|
SubmissionResponseError::Save(inner) => inner.error_response(),
|
||||||
SubmissionError::Form(inner) => inner.error_response(),
|
SubmissionResponseError::Render(inner) => inner.error_response(),
|
||||||
SubmissionError::Save(inner) => inner.error_response(),
|
SubmissionResponseError::MissingLinkOrAttachment
|
||||||
SubmissionError::Render(inner) => inner.error_response(),
|
| SubmissionResponseError::Form(_)
|
||||||
SubmissionError::TooManyRequests => default_error_response(self, status_code),
|
| SubmissionResponseError::TooManyRequests => default_error_response(self, status_code),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub(crate) enum SyncError {
|
pub(crate) enum SyncResponseError {
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
LoginRequired(#[from] LoginRequired),
|
Login(#[from] LoginRequired),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
Load(#[from] JobofferLoadError),
|
Load(#[from] JobofferLoadError),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
Presentation(#[from] PresentationError),
|
Presentation(#[from] PresentationError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseError for SyncError {
|
impl ResponseError for SyncResponseError {
|
||||||
fn status_code(&self) -> StatusCode {
|
fn status_code(&self) -> StatusCode {
|
||||||
match self {
|
match self {
|
||||||
SyncError::LoginRequired(_) => StatusCode::FORBIDDEN,
|
SyncResponseError::Login(_) => StatusCode::UNAUTHORIZED,
|
||||||
SyncError::Load(_) | SyncError::Presentation(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
SyncResponseError::Load(_) | SyncResponseError::Presentation(_) => {
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub(crate) enum AttachmentError {
|
pub(crate) enum AttachmentResponseError {
|
||||||
#[error("Job Offer not found!")]
|
#[error("Job Offer not found!")]
|
||||||
OfferNotFound,
|
OfferNotFound,
|
||||||
#[error("Viewing this Job Offers Attachments requires Authentication, as the offer is not yet published: {0}")]
|
#[error("Viewing this Job Offers Attachments requires Authentication, as the offer is not yet published: {0}")]
|
||||||
|
|
@ -205,24 +225,26 @@ pub(crate) enum AttachmentError {
|
||||||
IO(#[from] std::io::Error),
|
IO(#[from] std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseError for AttachmentError {
|
impl ResponseError for AttachmentResponseError {
|
||||||
fn status_code(&self) -> StatusCode {
|
fn status_code(&self) -> StatusCode {
|
||||||
match self {
|
match self {
|
||||||
AttachmentError::OfferNotFound => StatusCode::NOT_FOUND,
|
AttachmentResponseError::OfferNotFound => StatusCode::NOT_FOUND,
|
||||||
AttachmentError::IO(error) if error.kind() == ErrorKind::NotFound => {
|
AttachmentResponseError::IO(error) if error.kind() == ErrorKind::NotFound => {
|
||||||
StatusCode::NOT_FOUND
|
StatusCode::NOT_FOUND
|
||||||
}
|
}
|
||||||
AttachmentError::LoginRequired(inner) => inner.status_code(),
|
AttachmentResponseError::LoginRequired(_) => StatusCode::UNAUTHORIZED,
|
||||||
AttachmentError::IO(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
AttachmentResponseError::IO(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error_response(&self) -> HttpResponse<BoxBody> {
|
fn error_response(&self) -> HttpResponse<BoxBody> {
|
||||||
let status_code = self.status_code();
|
let status_code = self.status_code();
|
||||||
match self {
|
match self {
|
||||||
AttachmentError::OfferNotFound => default_error_response(self, status_code),
|
AttachmentResponseError::OfferNotFound => default_error_response(self, status_code),
|
||||||
AttachmentError::LoginRequired(inner) => inner.error_response(),
|
AttachmentResponseError::LoginRequired(_inner) => {
|
||||||
AttachmentError::IO(inner) => default_error_response(inner, status_code),
|
default_error_response(self, status_code)
|
||||||
|
}
|
||||||
|
AttachmentResponseError::IO(inner) => default_error_response(inner, status_code),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue