qlty_coverage/publish/
upload.rs

1use crate::publish::Report;
2use anyhow::{Context, Result};
3use qlty_cloud::{export::CoverageExport, Client as QltyClient};
4use std::path::PathBuf;
5
6#[derive(Default, Clone, Debug)]
7pub struct Upload {
8    pub id: String,
9    pub project_id: String,
10    pub file_coverages_url: String,
11    pub report_files_url: String,
12    pub metadata_url: String,
13}
14
15impl Upload {
16    pub fn prepare(token: &str, report: &mut Report) -> Result<Self> {
17        let client = QltyClient::new(token);
18
19        let response = client
20            .post("/coverage")
21            .send_json(ureq::json!({
22                "data": report.metadata,
23            }))?
24            .into_json::<serde_json::Value>()?;
25
26        let file_coverages_url = response
27            .get("data")
28            .and_then(|data| data.get("file_coverages.json.gz"))
29            .and_then(|upload_url| upload_url.as_str())
30            .with_context(|| {
31                format!(
32                    "Unable to find file coverages URL in response body: {:?}",
33                    response
34                )
35            })
36            .context("Failed to extract file coverages URL from response")?;
37
38        let report_files_url = response
39            .get("data")
40            .and_then(|data| data.get("report_files.json.gz"))
41            .and_then(|upload_url| upload_url.as_str())
42            .with_context(|| {
43                format!(
44                    "Unable to find report files URL in response body: {:?}",
45                    response
46                )
47            })
48            .context("Failed to extract report files URL from response")?;
49
50        let metadata_url = response
51            .get("data")
52            .and_then(|data| data.get("metadata.json"))
53            .and_then(|upload_url| upload_url.as_str())
54            .with_context(|| {
55                format!(
56                    "Unable to find metadata URL in response body: {:?}",
57                    response
58                )
59            })
60            .context("Failed to extract metadata URL from response")?;
61
62        let id = response
63            .get("data")
64            .and_then(|data| data.get("id"))
65            .and_then(|upload_url| upload_url.as_str())
66            .with_context(|| format!("Unable to find upload ID in response body: {:?}", response))
67            .context("Failed to extract upload ID from response")?;
68
69        let project_id = response
70            .get("data")
71            .and_then(|data| data.get("projectId"))
72            .and_then(|project_id| project_id.as_str())
73            .with_context(|| format!("Unable to find project ID in response body: {:?}", response))
74            .context("Failed to extract project ID from response")?;
75
76        report.set_upload_id(&id);
77        report.set_project_id(&project_id);
78
79        Ok(Self {
80            id: id.to_string(),
81            project_id: project_id.to_string(),
82            file_coverages_url: file_coverages_url.to_string(),
83            report_files_url: report_files_url.to_string(),
84            metadata_url: metadata_url.to_string(),
85        })
86    }
87
88    pub fn upload(&self, export: &CoverageExport) -> Result<()> {
89        self.upload_data(
90            &self.file_coverages_url,
91            "application/gzip",
92            export.read_file(&PathBuf::from("file_coverages.json.gz"))?,
93        )?;
94
95        self.upload_data(
96            &self.report_files_url,
97            "application/gzip",
98            export.read_file(&PathBuf::from("report_files.json.gz"))?,
99        )?;
100
101        self.upload_data(
102            &self.metadata_url,
103            "application/json",
104            export.read_file(&PathBuf::from("metadata.json"))?,
105        )?;
106
107        Ok(())
108    }
109
110    fn upload_data(
111        &self,
112        url: &str,
113        content_type: &str,
114        data: Vec<u8>,
115    ) -> Result<(), anyhow::Error> {
116        let response = ureq::put(url)
117            .set("Content-Type", content_type)
118            .send_bytes(&data)
119            .context("Failed to send PUT request")?;
120
121        if response.status() < 200 || response.status() >= 300 {
122            let error_message = format!(
123                "PUT request for uploading file returned {} status with response: {:?}",
124                response.status(),
125                response
126                    .into_string()
127                    .unwrap_or_else(|_| "Unknown error".to_string())
128            );
129            return Err(anyhow::anyhow!(error_message));
130        }
131
132        Ok(())
133    }
134}