Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make SQL entities to create their projection #625

Merged
merged 1 commit into from
Nov 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 20 additions & 36 deletions mithril-common/src/database/db_version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::{
use chrono::NaiveDateTime;
use sqlite::{Connection, Row, Value};

use crate::sqlite::{HydrationError, Projection, ProjectionField, Provider, SqLiteEntity};
use crate::sqlite::{HydrationError, Projection, Provider, SqLiteEntity};

use super::DbVersion;

Expand Down Expand Up @@ -69,35 +69,9 @@ impl SqLiteEntity for DatabaseVersion {
.map_err(|e| HydrationError::InvalidData(format!("{}", e)))?,
})
}
}

impl PartialOrd for DatabaseVersion {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.application_type != other.application_type {
None
} else {
self.version.partial_cmp(&other.version)
}
}
}

/// Projection dedicated to [DatabaseVersion] entities.
struct DatabaseVersionProjection {
fields: Vec<ProjectionField>,
}

impl Projection for DatabaseVersionProjection {
fn set_field(&mut self, field: ProjectionField) {
self.fields.push(field);
}

fn get_fields(&self) -> &Vec<ProjectionField> {
&self.fields
}
}
impl DatabaseVersionProjection {
pub fn new() -> Self {
let mut projection = Self { fields: Vec::new() };
fn get_projection() -> Projection {
let mut projection = Projection::default();
projection.add_field("version", "{:db_version:}.version", "text");
projection.add_field(
"application_type",
Expand All @@ -110,18 +84,28 @@ impl DatabaseVersionProjection {
}
}

impl PartialOrd for DatabaseVersion {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.application_type != other.application_type {
None
} else {
self.version.partial_cmp(&other.version)
}
}
}

/// Provider for the [DatabaseVersion] entities using the `DatabaseVersionProjection`.
pub struct DatabaseVersionProvider<'conn> {
connection: &'conn Connection,
projection: DatabaseVersionProjection,
projection: Projection,
}

impl<'conn> DatabaseVersionProvider<'conn> {
/// [DatabaseVersionProvider] constructor.
pub fn new(connection: &'conn Connection) -> Self {
Self {
connection,
projection: DatabaseVersionProjection::new(),
projection: DatabaseVersion::get_projection(),
}
}

Expand Down Expand Up @@ -169,7 +153,7 @@ insert into db_version (application_type, version) values ('{application_type}',
impl<'conn> Provider<'conn> for DatabaseVersionProvider<'conn> {
type Entity = DatabaseVersion;

fn get_projection(&self) -> &dyn Projection {
fn get_projection(&self) -> &Projection {
&self.projection
}

Expand Down Expand Up @@ -197,15 +181,15 @@ where {where_clause}
/// This will perform an UPSERT and return the updated entity.
pub struct DatabaseVersionUpdater<'conn> {
connection: &'conn Connection,
projection: DatabaseVersionProjection,
projection: Projection,
}

impl<'conn> DatabaseVersionUpdater<'conn> {
/// [DatabaseVersionUpdater] constructor.
pub fn new(connection: &'conn Connection) -> Self {
Self {
connection,
projection: DatabaseVersionProjection::new(),
projection: DatabaseVersion::get_projection(),
}
}

Expand All @@ -227,7 +211,7 @@ impl<'conn> DatabaseVersionUpdater<'conn> {
impl<'conn> Provider<'conn> for DatabaseVersionUpdater<'conn> {
type Entity = DatabaseVersion;

fn get_projection(&self) -> &dyn Projection {
fn get_projection(&self) -> &Projection {
&self.projection
}

Expand Down Expand Up @@ -257,7 +241,7 @@ mod tests {

#[test]
fn test_projection() {
let projection = DatabaseVersionProjection::new();
let projection = DatabaseVersion::get_projection();
let mut aliases: HashMap<String, String> = HashMap::new();
let _ = aliases.insert("{:db_version:}".to_string(), "whatever".to_string());

Expand Down
5 changes: 5 additions & 0 deletions mithril-common/src/sqlite/entity.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use sqlite::Row;
use thiserror::Error;

use super::Projection;

/// SqLite hydration error
#[derive(Error, Debug, Clone)]
pub enum HydrationError {
Expand All @@ -25,4 +27,7 @@ pub trait SqLiteEntity {
fn hydrate(row: Row) -> Result<Self, HydrationError>
where
Self: Sized;

/// Construct a [Projection] that will allow to hydrate this `SqLiteEntity`.
fn get_projection() -> Projection;
}
65 changes: 28 additions & 37 deletions mithril-common/src/sqlite/projection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,27 +39,34 @@ impl ProjectionField {
/// Projection is a definition of field mapping during a query.
/// Fields come from one or several source structures (can be tables, views or
/// sub queries) and are mapped to a Provider query as output.
pub trait Projection {
pub struct Projection {
fields: Vec<ProjectionField>,
}

impl Projection {
/// Instanciate a new Projection
pub fn new(fields: Vec<ProjectionField>) -> Self {
Self { fields }
}

/// Add a new field to the definition. This is one of the projection
/// building tool to create a projection out of an existing structure.
/// This is a blanket implementation.
fn add_field(&mut self, field_name: &str, definition: &str, output_type: &str) {
self.set_field(ProjectionField {
pub fn add_field(&mut self, field_name: &str, definition: &str, output_type: &str) {
self.fields.push(ProjectionField {
name: field_name.to_string(),
definition: definition.to_string(),
output_type: output_type.to_string(),
})
}

/// This method is requested by `add_field` to actually save the state in
/// the current Provider implementation.
fn set_field(&mut self, field: ProjectionField);

/// Returns the list of the ProjectionFields of this Projection.
fn get_fields(&self) -> &Vec<ProjectionField>;
pub fn get_fields(&self) -> &Vec<ProjectionField> {
&self.fields
}

/// Turn the Projection into a string suitable for use in SQL queries.
fn expand(&self, aliases: HashMap<String, String>) -> String {
pub fn expand(&self, aliases: HashMap<String, String>) -> String {
let mut fields: String = self
.get_fields()
.iter()
Expand All @@ -75,40 +82,24 @@ pub trait Projection {
}
}

impl Default for Projection {
fn default() -> Self {
Self::new(Vec::new())
}
}

#[cfg(test)]
mod tests {
use super::*;

struct TestProjection {
fields: Vec<ProjectionField>,
}

impl TestProjection {
pub fn new() -> Self {
let mut projection = Self { fields: Vec::new() };

projection.add_field("test_id", "{:test:}.test_id", "integer");
projection.add_field("name", "{:test:}.name", "text");
projection.add_field("created_at", "{:test:}.created_at", "timestamp");
projection.add_field("thing_count", "count({:thing:}.*)", "integer");

projection
}
}

impl Projection for TestProjection {
fn get_fields(&self) -> &Vec<ProjectionField> {
&self.fields
}

fn set_field(&mut self, field: ProjectionField) {
self.fields.push(field);
}
}

#[test]
fn simple_projection() {
let projection = TestProjection::new();
let mut projection = Projection::default();
projection.add_field("test_id", "{:test:}.test_id", "integer");
projection.add_field("name", "{:test:}.name", "text");
projection.add_field("created_at", "{:test:}.created_at", "timestamp");
projection.add_field("thing_count", "count({:thing:}.*)", "integer");

let aliases: HashMap<String, String> = [("{:test:}", "pika"), ("{:thing:}", "thing_alias")]
.into_iter()
.map(|(a, b)| (a.to_string(), b.to_string()))
Expand Down
36 changes: 10 additions & 26 deletions mithril-common/src/sqlite/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub trait Provider<'conn> {
fn get_connection(&'conn self) -> &'conn Connection;

/// Share the projection.
fn get_projection(&self) -> &dyn Projection;
fn get_projection(&self) -> &Projection;

/// Perform the parametrized definition query.
fn find(
Expand All @@ -41,7 +41,7 @@ pub trait Provider<'conn> {

#[cfg(test)]
mod tests {
use super::super::{entity::HydrationError, ProjectionField, SqLiteEntity};
use super::super::{entity::HydrationError, SqLiteEntity};
use super::*;

#[derive(Debug, PartialEq)]
Expand All @@ -61,25 +61,9 @@ mod tests {
maybe_null: row.get::<Option<i64>, _>(3),
})
}
}

pub struct TestProjection {
fields: Vec<ProjectionField>,
}

impl Projection for TestProjection {
fn get_fields(&self) -> &Vec<ProjectionField> {
&self.fields
}

fn set_field(&mut self, field: ProjectionField) {
self.fields.push(field);
}
}

impl TestProjection {
pub fn new() -> Self {
let mut projection = Self { fields: Vec::new() };
fn get_projection() -> Projection {
let mut projection = Projection::default();

projection.add_field("text_data", "{:test:}.text_data", "text");
projection.add_field("real_data", "{:test:}.real_data", "real");
Expand All @@ -92,14 +76,14 @@ mod tests {

struct TestEntityProvider {
connection: Connection,
projection: TestProjection,
projection: Projection,
}

impl TestEntityProvider {
pub fn new(connection: Connection) -> Self {
Self {
connection,
projection: TestProjection::new(),
projection: TestEntity::get_projection(),
}
}
}
Expand All @@ -111,7 +95,7 @@ mod tests {
&self.connection
}

fn get_projection(&self) -> &dyn Projection {
fn get_projection(&self) -> &Projection {
&self.projection
}

Expand All @@ -128,14 +112,14 @@ mod tests {

struct TestEntityUpdateProvider {
connection: Connection,
projection: TestProjection,
projection: Projection,
}

impl TestEntityUpdateProvider {
pub fn new(connection: Connection) -> Self {
Self {
connection,
projection: TestProjection::new(),
projection: TestEntity::get_projection(),
}
}
}
Expand All @@ -147,7 +131,7 @@ mod tests {
&self.connection
}

fn get_projection(&self) -> &dyn Projection {
fn get_projection(&self) -> &Projection {
&self.projection
}

Expand Down