133 lines
4.1 KiB
Rust
133 lines
4.1 KiB
Rust
//! Migration to update the patch `reviews` JSON representation.
|
|
use crate::cob::cache::*;
|
|
use serde_json as json;
|
|
|
|
/// Run migration.
|
|
pub fn run(
|
|
db: &sql::Connection,
|
|
migration: &Progress,
|
|
callback: &mut dyn MigrateCallback,
|
|
) -> Result<usize, Error> {
|
|
// Select patches with reviews.
|
|
let rows = db
|
|
.prepare("SELECT id, patch FROM patches WHERE json_extract(patch, '$.reviews') != '{}'")?
|
|
.into_iter()
|
|
.collect::<Vec<_>>();
|
|
// Query to update a patch to the new schema.
|
|
let mut update = db.prepare(
|
|
"UPDATE patches
|
|
SET patch = ?1
|
|
WHERE id = ?2",
|
|
)?;
|
|
let mut progress = Progress::new(rows.len());
|
|
callback.progress(MigrateProgress {
|
|
migration,
|
|
rows: &progress,
|
|
});
|
|
|
|
for row in rows {
|
|
let row = row?;
|
|
let id = row.try_read::<&str, _>("id")?;
|
|
let mut patch = json::from_str::<json::Value>(row.try_read::<&str, _>("patch")?)
|
|
.map_err(Error::MalformedJson)?;
|
|
let patch = patch.as_object_mut().ok_or(Error::MalformedJsonSchema)?;
|
|
let revisions = patch["revisions"]
|
|
.as_object_mut()
|
|
.ok_or(Error::MalformedJsonSchema)?;
|
|
let mut transformed = false;
|
|
|
|
for (_, r) in revisions.iter_mut() {
|
|
let Some(revision) = r.as_object_mut() else {
|
|
// Redacted revision (`null`).
|
|
continue;
|
|
};
|
|
let reviews = revision
|
|
.get_mut("reviews")
|
|
.ok_or(Error::MalformedJsonSchema)?
|
|
.as_object_mut()
|
|
.ok_or(Error::MalformedJsonSchema)?;
|
|
|
|
for (_, review) in reviews.iter_mut() {
|
|
if let Some(list) = review.as_array_mut()
|
|
&& let Some(last) = list.pop()
|
|
{
|
|
*review = last;
|
|
transformed = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if transformed {
|
|
let obj = json::to_string(&patch).map_err(Error::MalformedJson)?;
|
|
|
|
update.reset()?;
|
|
update.bind((1, obj.as_str()))?;
|
|
update.bind((2, id))?;
|
|
update.next()?;
|
|
progress.inc();
|
|
|
|
callback.progress(MigrateProgress {
|
|
migration,
|
|
rows: &progress,
|
|
});
|
|
}
|
|
}
|
|
Ok(progress.current())
|
|
}
|
|
|
|
#[allow(clippy::unwrap_used)]
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::cob::cache::*;
|
|
|
|
// Before the migration.
|
|
const PATCH_V1: &str = include_str!("samples/patch.v1.json");
|
|
// After the migration.
|
|
const PATCH_V2: &str = include_str!("samples/patch.v2.json");
|
|
|
|
#[test]
|
|
fn test_migration_2() {
|
|
let mut db = StoreWriter::memory().unwrap();
|
|
db.migrate_to(1, migrate::ignore).unwrap();
|
|
db.raw_query(|conn| {
|
|
let mut stmt = conn.prepare(
|
|
"INSERT INTO patches (id, repo, patch)
|
|
VALUES (?1, ?2, ?3)",
|
|
)?;
|
|
stmt.bind((1, "016a91d2029ee71b9aee8d927664caf1b7885346"))?;
|
|
stmt.bind((2, "rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5"))?;
|
|
stmt.bind((3, PATCH_V1))?;
|
|
stmt.next()?;
|
|
|
|
Ok::<_, sql::Error>(())
|
|
})
|
|
.unwrap();
|
|
|
|
assert_eq!(db.migrate_to(2, migrate::ignore).unwrap(), 2);
|
|
|
|
let row = db
|
|
.raw_query(|conn| {
|
|
Ok::<_, sql::Error>(
|
|
conn.prepare("SELECT patch FROM patches LIMIT 1")?
|
|
.into_iter()
|
|
.next()
|
|
.unwrap()
|
|
.unwrap(),
|
|
)
|
|
})
|
|
.unwrap();
|
|
|
|
let patch = row.try_read::<&str, _>("patch").unwrap();
|
|
let actual: serde_json::Value = serde_json::from_str(patch).unwrap();
|
|
let expected: serde_json::Value = serde_json::from_str(PATCH_V2).unwrap();
|
|
|
|
assert_eq!(actual, expected);
|
|
}
|
|
|
|
#[test]
|
|
fn test_patch_json_deserialization() {
|
|
serde_json::from_str::<crate::cob::patch::Patch>(PATCH_V1).unwrap_err();
|
|
serde_json::from_str::<crate::cob::patch::Patch>(PATCH_V2).unwrap();
|
|
}
|
|
}
|