use diesel::prelude::*;
use diesel::sql_query;
use diesel::sql_types::{BigInt, Nullable, Text};
use crate::commits::store::diesel::{Commit, CommitStoreError};
use crate::error::InternalError;
use super::CommitStoreOperations;
#[derive(QueryableByName)]
struct CurrentCommit {
#[column_name = "commit_id"]
#[sql_type = "Text"]
pub commit_id: String,
#[column_name = "current_commit_number"]
#[sql_type = "BigInt"]
pub commit_num: i64,
#[column_name = "service_id"]
#[sql_type = "Nullable<Text>"]
pub service_id: Option<String>,
}
pub(in crate::commits) trait CommitStoreGetCurrentSericeCommitsOperation {
fn get_current_service_commits(&self) -> Result<Vec<Commit>, CommitStoreError>;
}
impl<'a, C> CommitStoreGetCurrentSericeCommitsOperation for CommitStoreOperations<'a, C>
where
C: diesel::Connection,
i64: diesel::deserialize::FromSql<diesel::sql_types::BigInt, C::Backend>,
String: diesel::deserialize::FromSql<diesel::sql_types::Text, C::Backend>,
{
fn get_current_service_commits(&self) -> Result<Vec<Commit>, CommitStoreError> {
sql_query(
r#"
WITH current_commits AS
(
SELECT service_id, max(commit_num) AS current_commit_number
FROM commits
GROUP BY service_id
)
SELECT commits.commit_id,
commits.commit_num AS current_commit_number,
commits.service_id
FROM current_commits
INNER JOIN commits
ON commits.service_id = current_commits.service_id
AND commits.commit_num = current_commits.current_commit_number
"#,
)
.load::<CurrentCommit>(self.conn)
.map(|values| {
values
.into_iter()
.filter_map(
|CurrentCommit {
commit_id,
commit_num,
service_id,
}| {
service_id.map(|service_id| Commit {
commit_id,
commit_num,
service_id: Some(service_id),
})
},
)
.collect::<Vec<_>>()
})
.map_err(|err| CommitStoreError::InternalError(InternalError::from_source(Box::new(err))))
}
}
#[cfg(all(test, feature = "sqlite"))]
mod tests {
use super::*;
use diesel::insert_into;
use crate::commits::store::diesel::{models::NewCommitModel, schema::commits};
use crate::migrations::run_sqlite_migrations;
#[test]
fn test_get_current_service_commits() -> Result<(), Box<dyn std::error::Error>> {
let conn = SqliteConnection::establish(":memory:")?;
run_sqlite_migrations(&conn)?;
insert_into(commits::table)
.values(vec![
NewCommitModel {
commit_id: "first:sawtooth".into(),
commit_num: 1,
service_id: None,
},
NewCommitModel {
commit_id: "first:service1".into(),
commit_num: 1,
service_id: Some("service1".into()),
},
NewCommitModel {
commit_id: "second:service1".into(),
commit_num: 2,
service_id: Some("service1".into()),
},
NewCommitModel {
commit_id: "first:service2".into(),
commit_num: 1,
service_id: Some("service2".into()),
},
])
.execute(&conn)?;
let ops = CommitStoreOperations::new(&conn);
let current_service_commits = ops.get_current_service_commits()?;
assert_eq!(
vec![
Commit {
commit_id: "second:service1".into(),
commit_num: 2,
service_id: Some("service1".into()),
},
Commit {
commit_id: "first:service2".into(),
commit_num: 1,
service_id: Some("service2".into()),
},
],
current_service_commits,
);
Ok(())
}
}