docs-site/content/docs/extras/upgrades.md
+++ title = "Upgrades" description = "" date = 2021-05-01T18:20:00+00:00 updated = 2021-05-01T18:20:00+00:00 draft = false weight = 4 sort_by = "weight" template = "docs/page.html"
[extra] lead = "" toc = true top = false flair =[] +++
Cargo.tomlcargo loco doctor inside your project to verify that your app and environment is compatible with the new versionAs always, if anything turns wrong, open an issue and ask for help.
Loco is built on top of great libraries. It's wise to be mindful of their versions in new releases of Loco, and their individual changelogs.
These are the major ones:
AppContext instead of Config in init_logger in the Hooks traitPR: #1418
If you are supplying an implementation of init_logger in your impl of the Hooks trait in order to set up your own logging, you will need to make the following change:
- fn init_logger(config: &config::Config, env: &Environment) -> Result<bool> {
+ fn init_logger(ctx: &AppContext) -> Result<bool> {
Any code in your init_logger implementation that makes use of the config can access it through ctx.config. In addition, you will also be able to access anything else in the AppContext, such as the new shared_store. The env parameter is also removed, as that is accessible from the AppContext as ctx.environment.
PR: #1359
Swap from using the loco custom email validator, to the builtin email validator from validator.
- #[validate(custom (function = "validation::is_valid_email"))]
+ #[validate(email(message = "invalid email"))]
pub email: String,
Two major changes have been made to the background job system:
The Redis background job system has been completely refactored, replacing the Sidekiq-compatible implementation with a new custom implementation. This provides greater flexibility and improved performance, but means:
A new tag-based job filtering system has been added to all background worker providers:
To upgrade to the new job system:
Process existing jobs:
Clean up old data:
FLUSHDB command)Update Loco:
PR: #1385
The cache API has been refactored to support storing and retrieving any serializable type, not just strings. This is a breaking change that requires updates to your code:
Serialize and Deserialize from serdeBefore:
// Get a string value from cache
let value = cache.get("key").await?;
// Insert or get with callback
let value = app_ctx.cache.get_or_insert("key", async {
Ok("value".to_string())
}).await.unwrap();
// Insert or get with expiry
let value = app_ctx.cache.get_or_insert_with_expiry("key", Duration::from_secs(300), async {
Ok("value".to_string())
}).await.unwrap();
After:
// Get a string value from cache - specify the type
let value = cache.get::<String>("key").await?;
// Direct insert with any serializable type
cache.insert("key", &"value".to_string()).await?;
// Insert or get with callback - specify return type
let value = app_ctx.cache.get_or_insert::<String, _>("key", async {
Ok("value".to_string())
}).await.unwrap();
// Store complex types
#[derive(Serialize, Deserialize)]
struct User {
name: String,
age: u32,
}
let user = app_ctx.cache.get_or_insert_with_expiry::<User, _>(
"user:1",
Duration::from_secs(300),
async {
Ok(User { name: "Alice".to_string(), age: 30 })
}
).await.unwrap();
For your custom types to work with the cache, ensure they implement Serialize and Deserialize:
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct MyType {
// fields...
}
Authentication error handling has been improved to better distinguish between actual authorization failures and system errors:
tracing::errorIf you have code that relies on database errors during authentication returning 401 status codes, you'll need to update your error handling. Any code expecting a 401 for database connectivity issues should now handle 500 responses as well.
Client applications should be prepared to handle both 401 and 500 status codes during authentication failures, with 401 indicating authorization problems and 500 indicating system errors.
We had some changes in Tera template. go to src/initializers/view_engine.rs and replace the after_routes function with:
async fn after_routes(&self, router: AxumRouter, _ctx: &AppContext) -> Result<AxumRouter> {
let tera_engine = if std::path::Path::new(I18N_DIR).exists() {
let arc = std::sync::Arc::new(
ArcLoader::builder(&I18N_DIR, unic_langid::langid!("en-US"))
.shared_resources(Some(&[I18N_SHARED.into()]))
.customize(|bundle| bundle.set_use_isolating(false))
.build()
.map_err(|e| Error::string(&e.to_string()))?,
);
info!("locales loaded");
engines::TeraView::build()?.post_process(move |tera| {
tera.register_function("t", FluentLoader::new(arc.clone()));
Ok(())
})?
} else {
engines::TeraView::build()?
};
Ok(router.layer(Extension(ViewEngine::from(tera_engine))))
}
PR: #1199
Update the validator crate version in your Cargo.toml:
From
validator = { version = "0.19" }
To
validator = { version = "0.20" }
PR: #1159
Flattened (De)Serialization of Custom User Claims:
The claims field in UserClaims has changed from Option<Value> to Map<String, Value>.
Mandatory Map Value in generate_token function:
When calling generate_token, the Map<String, Value> argument is now required. If you are not using custom claims, pass an empty map (serde_json::Map::new()).
Updated generate_token Signature:
The generate_token function now takes expiration as a value instead of a reference.
PR: #1197
The pagination response now includes the total_items field, providing the total number of items available.
{"results":[],"pagination":{"page":0,"page_size":0,"total_pages":0,"total_items":0}}
PR: #1268
Migrations using create_table now require ("id", ColType::PkAuto), new migrations will have this field automatically added.
async fn up(&self, m: &SchemaManager) -> Result<(), DbErr> {
create_table(m, "movies",
&[
+ ("id", ColType::PkAuto),
("title", ColType::StringNull),
],
&[
("user", ""),
]
).await
}
PR: #1130 The upgrade to Axum 0.8 introduces a breaking change. For more details, refer to the announcement.
Cargo.toml, update the Axum version from 0.7.5 to 0.8.1.axum::async_trait; with use async_trait::async_trait;. For more information, see here./:single and /*many to /{single} and /{*many}.boot Function HookPR: #1143
The boot hook function now accepts an additional Config parameter. The function signature has changed from:
From
async fn boot(mode: StartMode, environment: &Environment) -> Result<BootResult> {
create_app::<Self, Migrator>(mode, environment).await
}
To:
async fn boot(mode: StartMode, environment: &Environment, config: Config) -> Result<BootResult> {
create_app::<Self, Migrator>(mode, environment, config).await
}
Make sure to import the Config type as needed.
PR: #993
Update the validator crate version in your Cargo.toml:
From
validator = { version = "0.18" }
To
validator = { version = "0.19" }
PR: #1158
The truncate and seed functions now receive AppContext instead of DatabaseConnection as their argument.
From
async fn truncate(db: &DatabaseConnection) -> Result<()> {}
async fn seed(db: &DatabaseConnection, base: &Path) -> Result<()> {}
To
async fn truncate(ctx: &AppContext) -> Result<()> {}
async fn seed(_ctx: &AppContext, base: &Path) -> Result<()> {}
Impact on Testing:
Testing code involving the seed function must also be updated accordingly.
from:
async fn load_page() {
request::<App, _, _>(|request, ctx| async move {
seed::<App>(&ctx.db).await.unwrap();
...
})
.await;
}
to
async fn load_page() {
request::<App, _, _>(|request, ctx| async move {
seed::<App>(&ctx).await.unwrap();
...
})
.await;
}