Skip to content

Commit 09a2168

Browse files
committed
feat: upload details model with flags, etc
1 parent 12631d5 commit 09a2168

File tree

5 files changed

+151
-13
lines changed

5 files changed

+151
-13
lines changed

migrations/01-init/down.sql

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
DROP TABLE upload_details;
12
DROP TABLE context_assoc;
23
DROP TABLE branches_data;
34
DROP TABLE method_data;

migrations/01-init/up.sql

+16
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,19 @@ CREATE TABLE context_assoc (
5858
span_id BLOB,
5959
PRIMARY KEY(context_id, sample_id, branch_id, method_id, span_id)
6060
);
61+
62+
CREATE TABLE upload_details (
63+
context_id INTEGER REFERENCES context(id) NOT NULL,
64+
timestamp INTEGER,
65+
raw_upload_url VARCHAR,
66+
flags VARCHAR, -- JSON
67+
provider VARCHAR,
68+
build VARCHAR,
69+
name VARCHAR,
70+
job_name VARCHAR,
71+
ci_run_url VARCHAR,
72+
state VARCHAR,
73+
env VARCHAR,
74+
session_type VARCHAR,
75+
session_extras VARCHAR -- JSON,
76+
);

src/report/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ pub trait Report {
2222
&self,
2323
file: &models::SourceFile,
2424
) -> Result<Vec<models::CoverageSample>>;
25+
fn get_details_for_upload(&self, upload: &models::Context) -> Result<models::UploadDetails>;
2526

2627
fn merge(&mut self, other: &Self) -> Result<()>;
2728
}
@@ -85,5 +86,11 @@ pub trait ReportBuilder<R: Report> {
8586
span_data: Option<&'a models::SpanData>,
8687
) -> Result<models::ContextAssoc>;
8788

89+
fn insert_upload_details(
90+
&mut self,
91+
context_id: i64,
92+
upload_details: models::UploadDetails,
93+
) -> Result<models::UploadDetails>;
94+
8895
fn build(self) -> R;
8996
}

src/report/models.rs

+15-13
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput,
5656
use strum_macros::{Display, EnumString};
5757
use uuid::Uuid;
5858

59+
use crate::parsers::json::JsonVal;
60+
5961
#[derive(PartialEq, Debug, Clone, Copy, Default)]
6062
pub enum CoverageType {
6163
#[default]
@@ -320,17 +322,17 @@ pub struct Context {
320322
#[derive(PartialEq, Debug, Default)]
321323
pub struct UploadDetails {
322324
pub context_id: i64,
323-
324-
pub timestamp: i64,
325-
pub raw_upload_url: String,
326-
pub flags: Vec<String>,
327-
pub provider: String,
328-
pub build: String,
329-
pub name: String,
330-
pub job_name: String,
331-
pub ci_run_url: String,
332-
pub state: String,
333-
pub env: String,
334-
pub session_type: String,
335-
pub session_extras: String,
325+
pub timestamp: Option<i64>,
326+
pub raw_upload_url: Option<String>,
327+
/// JSON array
328+
pub flags: Option<JsonVal>,
329+
pub provider: Option<String>,
330+
pub build: Option<String>,
331+
pub name: Option<String>,
332+
pub job_name: Option<String>,
333+
pub ci_run_url: Option<String>,
334+
pub state: Option<String>,
335+
pub env: Option<String>,
336+
pub session_type: Option<String>,
337+
pub session_extras: Option<JsonVal>,
336338
}

src/report/sqlite_report.rs

+112
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use uuid::Uuid;
88

99
use crate::{
1010
error::Result,
11+
parsers::json::JsonVal,
1112
report::{models, Report, ReportBuilder},
1213
};
1314

@@ -30,6 +31,14 @@ fn open_database(filename: &PathBuf) -> Result<Connection> {
3031
Ok(conn)
3132
}
3233

34+
/// Can't implement foreign traits (`ToSql`/`FromSql`) on foreign types
35+
/// (`serde_json::Value`) so this helper function fills in.
36+
fn json_value_from_sql(s: String, col: usize) -> rusqlite::Result<Option<JsonVal>> {
37+
serde_json::from_str(s.as_str()).map_err(|e| {
38+
rusqlite::Error::FromSqlConversionFailure(col, rusqlite::types::Type::Text, Box::new(e))
39+
})
40+
}
41+
3342
impl SqliteReport {
3443
pub fn new(filename: PathBuf) -> Result<SqliteReport> {
3544
let conn = open_database(&filename)?;
@@ -156,6 +165,32 @@ impl Report for SqliteReport {
156165
Ok(result)
157166
}
158167

168+
fn get_details_for_upload(&self, upload: &models::Context) -> Result<models::UploadDetails> {
169+
assert_eq!(upload.context_type, models::ContextType::Upload);
170+
let mut stmt = self.conn.prepare("SELECT context_id, timestamp, raw_upload_url, flags, provider, build, name, job_name, ci_run_url, state, env, session_type, session_extras FROM upload_details WHERE context_id = ?1")?;
171+
Ok(stmt.query_row([upload.id], |row| {
172+
Ok(models::UploadDetails {
173+
context_id: row.get(0)?,
174+
timestamp: row.get(1)?,
175+
raw_upload_url: row.get(2)?,
176+
flags: row
177+
.get::<usize, String>(3)
178+
.and_then(|s| json_value_from_sql(s, 3))?,
179+
provider: row.get(4)?,
180+
build: row.get(5)?,
181+
name: row.get(6)?,
182+
job_name: row.get(7)?,
183+
ci_run_url: row.get(8)?,
184+
state: row.get(9)?,
185+
env: row.get(10)?,
186+
session_type: row.get(11)?,
187+
session_extras: row
188+
.get::<usize, String>(12)
189+
.and_then(|s| json_value_from_sql(s, 12))?,
190+
})
191+
})?)
192+
}
193+
159194
/// Merge `other` into `self` without modifying `other`.
160195
///
161196
/// TODO: Probably put this in a commit
@@ -413,6 +448,35 @@ impl ReportBuilder<SqliteReport> for SqliteReportBuilder {
413448
)?)
414449
}
415450

451+
fn insert_upload_details(
452+
&mut self,
453+
context_id: i64,
454+
mut upload_details: models::UploadDetails,
455+
) -> Result<models::UploadDetails> {
456+
upload_details.context_id = context_id;
457+
let mut stmt = self.conn.prepare("INSERT INTO upload_details (context_id, timestamp, raw_upload_url, flags, provider, build, name, job_name, ci_run_url, state, env, session_type, session_extras) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13)")?;
458+
let _ = stmt.execute((
459+
&upload_details.context_id,
460+
&upload_details.timestamp,
461+
&upload_details.raw_upload_url,
462+
&upload_details.flags.as_ref().map(|v| v.to_string()),
463+
&upload_details.provider,
464+
&upload_details.build,
465+
&upload_details.name,
466+
&upload_details.job_name,
467+
&upload_details.ci_run_url,
468+
&upload_details.state,
469+
&upload_details.env,
470+
&upload_details.session_type,
471+
&upload_details
472+
.session_extras
473+
.as_ref()
474+
.map(|v| v.to_string()),
475+
))?;
476+
477+
Ok(upload_details)
478+
}
479+
416480
fn build(self) -> SqliteReport {
417481
SqliteReport {
418482
filename: self.filename,
@@ -652,6 +716,8 @@ mod tests {
652716
}
653717

654718
mod sqlite_report_builder {
719+
use serde_json::{json, json_internal};
720+
655721
use super::*;
656722

657723
fn hash_id(key: &str) -> i64 {
@@ -949,5 +1015,51 @@ mod tests {
9491015
.unwrap();
9501016
assert_eq!(actual_assoc, expected_assoc);
9511017
}
1018+
1019+
#[test]
1020+
fn test_insert_upload_details() {
1021+
let ctx = setup();
1022+
let db_file = ctx.temp_dir.path().join("db.sqlite");
1023+
let mut report_builder = SqliteReportBuilder::new(db_file).unwrap();
1024+
1025+
let upload = report_builder
1026+
.insert_context(models::ContextType::Upload, "codecov-rs CI")
1027+
.unwrap();
1028+
let inserted_details = models::UploadDetails {
1029+
context_id: upload.id,
1030+
timestamp: Some(123),
1031+
raw_upload_url: Some("https://example.com".to_string()),
1032+
flags: Some(json!(["abc".to_string(), "def".to_string()])),
1033+
provider: Some("provider".to_string()),
1034+
build: Some("build".to_string()),
1035+
name: Some("name".to_string()),
1036+
job_name: Some("job name".to_string()),
1037+
ci_run_url: Some("https://example.com".to_string()),
1038+
state: Some("state".to_string()),
1039+
env: Some("env".to_string()),
1040+
session_type: Some("uploaded".to_string()),
1041+
session_extras: Some(json!({})),
1042+
};
1043+
let inserted_details = report_builder
1044+
.insert_upload_details(upload.id, inserted_details)
1045+
.unwrap();
1046+
1047+
let other_upload = report_builder
1048+
.insert_context(models::ContextType::Upload, "codecov-rs CI 2")
1049+
.unwrap();
1050+
1051+
let report = report_builder.build();
1052+
let fetched_details = report.get_details_for_upload(&upload).unwrap();
1053+
assert_eq!(fetched_details, inserted_details);
1054+
1055+
let other_details_result = report.get_details_for_upload(&other_upload);
1056+
assert!(other_details_result.is_err());
1057+
match other_details_result {
1058+
Err(crate::error::CodecovError::SqliteError(
1059+
rusqlite::Error::QueryReturnedNoRows,
1060+
)) => {}
1061+
_ => assert!(false),
1062+
}
1063+
}
9521064
}
9531065
}

0 commit comments

Comments
 (0)