1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-07-31 09:24:31 +03:00

Simplify template render check logic

Also documents a bunch of things in mas_core::templates
This commit is contained in:
Quentin Gliech
2021-09-24 11:45:25 +02:00
parent a1c7b7f82d
commit e9158382ef
4 changed files with 62 additions and 30 deletions

View File

@ -59,7 +59,8 @@ impl TemplatesCommand {
}
SC::Check { path, skip_builtin } => {
Templates::load(Some(path), !skip_builtin)?;
let templates = Templates::load(Some(path), !skip_builtin)?;
templates.check_render()?;
Ok(())
}

View File

@ -22,6 +22,7 @@ use crate::{errors::ErroredForm, filters::CsrfToken, storage::SessionInfo};
/// Helper trait to construct context wrappers
pub trait TemplateContext {
/// Attach a user session to the template context
fn with_session(self, current_session: SessionInfo) -> WithSession<Self>
where
Self: Sized,
@ -32,6 +33,7 @@ pub trait TemplateContext {
}
}
/// Attach an optional user session to the template context
fn maybe_with_session(self, current_session: Option<SessionInfo>) -> WithOptionalSession<Self>
where
Self: Sized,
@ -42,6 +44,7 @@ pub trait TemplateContext {
}
}
/// Attach a CSRF token to the template context
fn with_csrf(self, token: &CsrfToken) -> WithCsrf<Self>
where
Self: Sized,
@ -52,6 +55,10 @@ pub trait TemplateContext {
}
}
/// Generate sample values for this context type
///
/// This is then used to check for template validity in unit tests and in
/// the CLI (`cargo run -- templates check`)
fn sample() -> Vec<Self>
where
Self: Sized;

View File

@ -32,10 +32,6 @@ macro_rules! register_templates {
extra = { $( $extra_template:expr ),* };
)?
$(
generics = { $( $generic:ident = $sample:ty ),* };
)?
$(
// Match any attribute on the function, such as #[doc], #[allow(dead_code)], etc.
$( #[ $attr:meta ] )*
@ -76,34 +72,31 @@ macro_rules! register_templates {
.map_err(|source| TemplateError::Render { template: $template, source })
}
)*
}
pub fn check_render(&self) -> Result<(), TemplateError> {
self.check_render_inner $( ::< $( $sample ),+ > )? ()
}
/// Helps rendering each template with sample data
pub mod check {
use super::*;
fn check_render_inner
$( < $( $generic ),+ > )?
(&self) -> Result<(), TemplateError>
$( where
$( $generic : TemplateContext + Serialize, )+
)?
$(
#[doc = concat!("Render the `", $template, "` template with sample contexts")]
pub fn $name
$(< $( $lt $( : $clt $(+ $dlt )* + TemplateContext )? ),+ >)?
(templates: &Templates)
-> anyhow::Result<()> {
let samples: Vec< $param > = TemplateContext::sample();
{
$(
{
let samples: Vec< $param > = TemplateContext::sample();
let name = $template;
for sample in samples {
::tracing::info!(name, "Rendering template");
self. $name (&sample)?;
}
let name = $template;
for sample in samples {
let context = serde_json::to_value(&sample)?;
::tracing::info!(name, %context, "Rendering template");
templates. $name (&sample)
.with_context(|| format!("Failed to render template {:?} with context {}", name, context))?;
}
)*
Ok(())
}
Ok(())
}
)*
}
};
}

View File

@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#![deny(missing_docs)]
//! Templates rendering
use std::{collections::HashSet, io::Cursor, path::Path, string::ToString, sync::Arc};
@ -25,7 +27,9 @@ use tokio::{fs::OpenOptions, io::AsyncWriteExt};
use tracing::{debug, info, warn};
use warp::reject::Reject;
#[allow(missing_docs)] // TODO
mod context;
#[macro_use]
mod macros;
@ -38,14 +42,19 @@ pub use self::context::{
#[derive(Debug, Clone)]
pub struct Templates(Arc<Tera>);
/// There was an issue while loading the templates
#[derive(Error, Debug)]
pub enum TemplateLoadingError {
/// Some templates failed to compile
#[error("could not load and compile some templates")]
Compile(#[from] TeraError),
/// There are essential templates missing
#[error("missing templates {missing:?}")]
MissingTemplates {
/// List of missing templates
missing: HashSet<String>,
/// List of templates that were loaded
loaded: HashSet<String>,
},
}
@ -146,18 +155,27 @@ impl Templates {
}
}
/// Failed to render a template
#[derive(Error, Debug)]
pub enum TemplateError {
/// Failed to prepare the context used by this template
#[error("could not prepare context for template {template:?}")]
Context {
/// The name of the template being rendered
template: &'static str,
/// The underlying error
#[source]
source: TeraError,
},
/// Failed to render the template
#[error("could not render template {template:?}")]
Render {
/// The name of the template being rendered
template: &'static str,
/// The underlying error
#[source]
source: TeraError,
},
@ -167,7 +185,6 @@ impl Reject for TemplateError {}
register_templates! {
extra = { "base.html" };
generics = { T = EmptyContext };
/// Render the login page
pub fn render_login(WithCsrf<LoginContext>) { "login.html" }
@ -188,12 +205,26 @@ register_templates! {
pub fn render_error(ErrorContext) { "error.html" }
}
impl Templates {
/// Render all templates with the generated samples to check if they render
/// properly
pub fn check_render(&self) -> anyhow::Result<()> {
check::render_login(self)?;
check::render_register(self)?;
check::render_index(self)?;
check::render_reauth(self)?;
check::render_form_post::<EmptyContext>(self)?;
check::render_error(self)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_all_templates() {
fn check_builtin_templates() {
let templates = Templates::load(None, true).unwrap();
templates.check_render().unwrap();
}