Add download endpoint to EdenApi trait

Summary:
This adds the plumbing to access download a file using the endpoint from the previous diff via the EdenApi trait, which does the actual http request.

It concats the stream into a Bytes object and returns it.

Reviewed By: StanislavGlebik

Differential Revision: D30582422

fbshipit-source-id: ed0fe5e34e3fecc6c1b26d2dceb322dfcf5f8e37
This commit is contained in:
Yan Soares Couto 2021-09-09 10:02:57 -07:00 committed by Facebook GitHub Bot
parent b5184cbb2d
commit 56d4129e8c
5 changed files with 58 additions and 2 deletions

View File

@ -564,6 +564,14 @@ impl EdenApi for EagerRepo {
.map_err(|e| EdenApiError::Other(e.into()))?;
Ok(convert_to_response(hashes))
}
async fn download_file(
&self,
_repo: String,
_token: UploadToken,
) -> Result<Bytes, EdenApiError> {
unimplemented!()
}
}
impl EagerRepo {

View File

@ -15,6 +15,7 @@ async-trait = "0.1.51"
atty = "0.2"
auth = { path = "../auth" }
blake2 = "0.8"
bytes = { version = "1.0", features = ["serde"] }
chrono = { version = "0.4", features = ["clock", "serde", "std"], default-features = false }
configmodel = { path = "../configmodel" }
configparser = { path = "../configparser" }

View File

@ -5,7 +5,9 @@
* GNU General Public License version 2.
*/
use bytes::Bytes as RawBytes;
use std::collections::{HashMap, HashSet};
use std::convert::TryInto;
use std::fmt::Debug;
use std::fs::{create_dir_all, File};
use std::iter::FromIterator;
@ -45,8 +47,8 @@ use edenapi_types::{
HgMutationEntryContent, HistoryEntry, HistoryRequest, LookupRequest, LookupResponse,
ServerError, ToApi, ToWire, TreeAttributes, TreeEntry, TreeRequest,
UploadBonsaiChangesetRequest, UploadHgChangeset, UploadHgChangesetsRequest,
UploadHgFilenodeRequest, UploadToken, UploadTokensResponse, UploadTreeEntry, UploadTreeRequest,
UploadTreeResponse,
UploadHgFilenodeRequest, UploadToken, UploadTokenMetadata, UploadTokensResponse,
UploadTreeEntry, UploadTreeRequest, UploadTreeResponse,
};
use hg_http::http_client;
use http_client::{AsyncResponse, HttpClient, HttpClientError, Progress, Request};
@ -97,6 +99,7 @@ mod paths {
pub const UPLOAD_BONSAI_CHANGESET: &str = "upload/changeset/bonsai";
pub const EPHEMERAL_PREPARE: &str = "ephemeral/prepare";
pub const FETCH_SNAPSHOT: &str = "snapshot";
pub const DOWNLOAD_FILE: &str = "download/file";
}
#[derive(Clone)]
@ -1241,6 +1244,39 @@ impl EdenApi for Client {
Ok(self.fetch::<WireFetchSnapshotResponse>(vec![request], None)?)
}
async fn download_file(&self, repo: String, token: UploadToken) -> Result<Bytes, EdenApiError> {
let download_file = "Downloading file";
tracing::info!("{}", download_file);
if self.config().debug {
eprintln!("{}", download_file);
}
let url = self.build_url(paths::DOWNLOAD_FILE, Some(&repo))?;
let metadata = token.data.metadata.clone();
let req = token.to_wire();
let request = self
.configure_request(Request::post(url.clone()))?
.cbor(&req)
.map_err(EdenApiError::RequestSerializationFailed)?;
use bytes::BytesMut;
let buf = if let Some(UploadTokenMetadata::FileContentTokenMetadata(m)) = metadata {
BytesMut::with_capacity(m.content_size.try_into().unwrap_or_default())
} else {
BytesMut::new()
};
Ok(self
.fetch::<RawBytes>(vec![request], None)?
.entries
.try_fold(buf, |mut buf, chunk| async move {
buf.extend_from_slice(&chunk);
Ok(buf)
})
.await?
.freeze()
.into())
}
}
/// Split up a collection of keys into batches of at most `batch_size`.

View File

@ -203,4 +203,7 @@ pub trait EdenApi: Send + Sync + 'static {
repo: String,
request: FetchSnapshotRequest,
) -> Result<Response<FetchSnapshotResponse>, EdenApiError>;
/// Download single file from upload token
async fn download_file(&self, repo: String, token: UploadToken) -> Result<Bytes, EdenApiError>;
}

View File

@ -523,6 +523,14 @@ impl EdenApi for FakeEdenApi {
) -> Result<Response<CommitHashLookupResponse>, EdenApiError> {
unimplemented!()
}
async fn download_file(
&self,
_repo: String,
_token: UploadToken,
) -> Result<Bytes, EdenApiError> {
unimplemented!()
}
}
pub fn make_config(dir: impl AsRef<Path>) -> ConfigSet {