Skip to content

Commit 6768818

Browse files
nipunn1313Convex, Inc.
authored andcommitted
Accept either ID or Snapshot TS for the get_zip_export endpoint (#31213)
We will want to prefer ID eventually, but accept both for now. GitOrigin-RevId: 9b1b4c70a04cb09ea2d021d6e99613bf2bd090cd
1 parent 7a1bc13 commit 6768818

File tree

8 files changed

+53
-40
lines changed

8 files changed

+53
-40
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ deno_core_icudata = "0.73.0"
3939
derive_more = "0.99"
4040
divan = "0.1.14"
4141
dotenvy = "0.15.7"
42+
either = "1.9.0"
4243
elliptic-curve = { version = "0.12.1", features = [ "std", "pem" ] }
4344
encoding_rs = "0.8.32"
4445
p256 = { version = "0.11.1", features = [ "ecdh" ] }

crates/application/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ common = { path = "../common" }
2121
convex_macro = { path = "../convex_macro" }
2222
csv-async = { workspace = true }
2323
database = { path = "../database" }
24+
either = { workspace = true }
2425
errors = { path = "../errors" }
2526
events = { path = "../events" }
2627
file_storage = { path = "../file_storage" }

crates/application/src/lib.rs

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ use common::{
5353
},
5454
document::{
5555
DocumentUpdate,
56-
ParsedDocument,
5756
CREATION_TIME_FIELD_PATH,
5857
},
5958
errors::{
@@ -122,6 +121,7 @@ use database::{
122121
Transaction,
123122
WriteSource,
124123
};
124+
use either::Either;
125125
use errors::{
126126
ErrorMetadata,
127127
ErrorMetadataAnyhowExt,
@@ -1373,39 +1373,32 @@ impl<RT: Runtime> Application<RT> {
13731373
pub async fn get_zip_export(
13741374
&self,
13751375
identity: Identity,
1376-
snapshot_ts: Timestamp,
1376+
id: Either<DeveloperDocumentId, Timestamp>,
13771377
) -> anyhow::Result<(StorageGetStream, String)> {
1378-
let stream = self.get_export_inner(identity, snapshot_ts).await?;
1379-
let filename = format!(
1380-
// This should match the format in SnapshotExport.tsx.
1381-
"snapshot_{}_{snapshot_ts}.zip",
1382-
self.instance_name
1383-
);
1384-
Ok((stream, filename))
1385-
}
1386-
1387-
async fn get_export_inner(
1388-
&self,
1389-
identity: Identity,
1390-
snapshot_ts: Timestamp,
1391-
) -> anyhow::Result<StorageGetStream> {
1392-
let object_key = {
1378+
let (object_key, snapshot_ts) = {
13931379
let mut tx = self.begin(identity).await?;
1394-
let export_doc = ExportsModel::new(&mut tx)
1395-
.completed_export_at_ts(snapshot_ts)
1396-
.await?;
1397-
let export: ParsedDocument<Export> = export_doc
1398-
.context(ErrorMetadata::not_found(
1399-
"ExportNotFound",
1400-
format!("The requested export {snapshot_ts} was not found"),
1401-
))?
1402-
.try_into()?;
1380+
let export = match id {
1381+
Either::Left(id) => ExportsModel::new(&mut tx).get(id).await?,
1382+
Either::Right(ts) => {
1383+
ExportsModel::new(&mut tx)
1384+
.completed_export_at_ts(ts)
1385+
.await?
1386+
},
1387+
}
1388+
.context(ErrorMetadata::not_found(
1389+
"ExportNotFound",
1390+
format!("The requested export {id} was not found"),
1391+
))?;
14031392
match export.into_value() {
1404-
Export::Completed { zip_object_key, .. } => zip_object_key,
1393+
Export::Completed {
1394+
zip_object_key,
1395+
start_ts,
1396+
..
1397+
} => (zip_object_key, start_ts),
14051398
Export::Failed { .. } | Export::InProgress { .. } | Export::Requested { .. } => {
14061399
anyhow::bail!(ErrorMetadata::bad_request(
14071400
"ExportNotComplete",
1408-
format!("The requested export {snapshot_ts} has not completed"),
1401+
format!("The requested export {id} has not completed"),
14091402
))
14101403
},
14111404
}
@@ -1418,7 +1411,13 @@ impl<RT: Runtime> Application<RT> {
14181411
"ExportNotFound",
14191412
format!("The requested export {snapshot_ts}/{object_key:?} was not found"),
14201413
))?;
1421-
Ok(storage_get_stream)
1414+
1415+
let filename = format!(
1416+
// This should match the format in SnapshotExport.tsx.
1417+
"snapshot_{}_{snapshot_ts}.zip",
1418+
self.instance_name
1419+
);
1420+
Ok((storage_get_stream, filename))
14221421
}
14231422

14241423
/// Returns the cloud export key - fully qualified to the instance.

crates/local_backend/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ clap = { workspace = true }
2121
cmd_util = { path = "../../crates/cmd_util" }
2222
common = { path = "../common" }
2323
database = { path = "../database" }
24+
either = { workspace = true }
2425
errors = { path = "../errors" }
2526
events = { path = "../events" }
2627
file_storage = { path = "../file_storage" }

crates/local_backend/src/router.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ pub fn router(st: LocalAppState) -> Router {
192192

193193
let snapshot_export_routes = Router::new()
194194
.route("/request/zip", post(request_zip_export))
195-
.route("/zip/:snapshot_ts", get(get_zip_export));
195+
.route("/zip/:id", get(get_zip_export));
196196

197197
let api_routes = Router::new()
198198
.merge(cli_routes)

crates/local_backend/src/snapshot_export.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use common::{
2424
HttpResponseError,
2525
},
2626
};
27+
use either::Either;
2728
use errors::ErrorMetadata;
2829
use http::StatusCode;
2930
use model::exports::types::{
@@ -33,6 +34,7 @@ use model::exports::types::{
3334
use serde::Deserialize;
3435
use storage::StorageGetStream;
3536
use sync_types::Timestamp;
37+
use value::DeveloperDocumentId;
3638

3739
use crate::{
3840
admin::must_be_admin_with_write_access,
@@ -77,28 +79,31 @@ pub async fn request_zip_export(
7779

7880
#[derive(Deserialize)]
7981
pub struct ZipExportRequest {
80-
// Timestamp the snapshot export started at
81-
snapshot_ts: String,
82+
// The ID of the snapshot
83+
id: String,
8284
}
8385

8486
#[debug_handler]
8587
pub async fn get_zip_export(
8688
State(st): State<LocalAppState>,
8789
ExtractIdentity(identity): ExtractIdentity,
88-
Path(ZipExportRequest { snapshot_ts }): Path<ZipExportRequest>,
90+
Path(ZipExportRequest { id }): Path<ZipExportRequest>,
8991
) -> Result<impl IntoResponse, HttpResponseError> {
9092
must_be_admin_with_write_access(&identity)?;
91-
let ts: Timestamp = snapshot_ts.parse().context(ErrorMetadata::bad_request(
92-
"BadSnapshotTimestamp",
93-
"Snapshot timestamp did not parse to a timestamp.",
94-
))?;
93+
let id: Either<DeveloperDocumentId, Timestamp> = match id.parse() {
94+
Ok(id) => Either::Left(id),
95+
Err(_) => Either::Right(id.parse().context(ErrorMetadata::bad_request(
96+
"BadSnapshotId",
97+
"Snapshot Id did not parse to an ID.",
98+
))?),
99+
};
95100
let (
96101
StorageGetStream {
97102
content_length,
98103
stream,
99104
},
100105
filename,
101-
) = st.application.get_zip_export(identity, ts).await?;
106+
) = st.application.get_zip_export(identity, id).await?;
102107
let content_length = ContentLength(content_length as u64);
103108
Ok((
104109
TypedHeader(content_length),

crates/model/src/exports/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ impl<'a, RT: Runtime> ExportsModel<'a, RT> {
215215
pub async fn completed_export_at_ts(
216216
&mut self,
217217
snapshot_ts: Timestamp,
218-
) -> anyhow::Result<Option<ResolvedDocument>> {
218+
) -> anyhow::Result<Option<ParsedDocument<Export>>> {
219219
let index_range = IndexRange {
220220
index_name: EXPORTS_BY_STATE_AND_TS_INDEX.clone(),
221221
range: vec![
@@ -229,7 +229,11 @@ impl<'a, RT: Runtime> ExportsModel<'a, RT> {
229229
};
230230
let query = Query::index_range(index_range);
231231
let mut query_stream = ResolvedQuery::new(self.tx, TableNamespace::Global, query)?;
232-
query_stream.expect_at_most_one(self.tx).await
232+
query_stream
233+
.expect_at_most_one(self.tx)
234+
.await?
235+
.map(|doc| doc.try_into())
236+
.transpose()
233237
}
234238

235239
pub async fn get(

0 commit comments

Comments
 (0)