posthog_cli/commands/sourcemap/
upload.rs

1use core::str;
2use std::path::PathBuf;
3
4use anyhow::{anyhow, Context, Ok, Result};
5use tracing::info;
6
7use crate::utils::auth::load_token;
8use crate::utils::sourcemaps::{read_pairs, ChunkUpload, SourcePair};
9
10pub fn upload(host: &str, directory: &PathBuf, _build_id: &Option<String>) -> Result<()> {
11    let token = load_token().context("While starting upload command")?;
12
13    let url = format!(
14        "{}/api/projects/{}/error_tracking/symbol_sets",
15        host, token.env_id
16    );
17
18    let pairs = read_pairs(directory)?;
19
20    let uploads = collect_uploads(pairs).context("While preparing files for upload")?;
21    info!("Found {} chunks to upload", uploads.len());
22
23    upload_chunks(&url, &token.token, uploads)?;
24
25    Ok(())
26}
27
28fn collect_uploads(pairs: Vec<SourcePair>) -> Result<Vec<ChunkUpload>> {
29    let uploads: Vec<ChunkUpload> = pairs
30        .into_iter()
31        .map(|pair| pair.into_chunk_upload())
32        .collect::<Result<Vec<ChunkUpload>>>()?;
33    Ok(uploads)
34}
35
36fn upload_chunks(url: &str, token: &str, uploads: Vec<ChunkUpload>) -> Result<()> {
37    let client = reqwest::blocking::Client::new();
38    for upload in uploads {
39        info!("Uploading chunk {}", upload.chunk_id);
40        let res = client
41            .post(url)
42            .header("Authorization", format!("Bearer {}", token))
43            .header("Content-Type", "application/octet-stream")
44            .header("Content-Disposition", "attachment; filename='chunk'")
45            .query(&[("chunk_id", &upload.chunk_id)])
46            .body(upload.data)
47            .send()
48            .context(format!("While uploading chunk to {}", url))?;
49
50        if !res.status().is_success() {
51            return Err(anyhow!("Failed to upload chunk: {:?}", res)
52                .context(format!("Chunk id: {}", upload.chunk_id)));
53        }
54    }
55
56    Ok(())
57}