Skip to content

Commit 08f03c3

Browse files
committed
handler for updating dataset tile
1 parent 92025bb commit 08f03c3

File tree

5 files changed

+280
-4
lines changed

5 files changed

+280
-4
lines changed

services/src/api/apidoc.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#![allow(clippy::needless_for_each)] // TODO: remove when clippy is fixed for utoipa <https://github.com/juhaku/utoipa/issues/1420>
22

33
use crate::api::handlers;
4-
use crate::api::handlers::datasets::{AddDatasetTile, DatasetTile, VolumeFileLayersResponse};
4+
use crate::api::handlers::datasets::{
5+
AddDatasetTile, DatasetTile, UpdateDatasetTile, VolumeFileLayersResponse,
6+
};
57
use crate::api::handlers::permissions::{
68
PermissionListOptions, PermissionListing, PermissionRequest, Resource,
79
};
@@ -68,6 +70,7 @@ use crate::api::{
6870
};
6971
use crate::contexts::SessionId;
7072
use crate::datasets::listing::{DatasetListing, OrderBy};
73+
use crate::datasets::postgres::DatasetTileId;
7174
use crate::datasets::storage::{AutoCreateDataset, SuggestMetaData};
7275
use crate::datasets::upload::{UploadId, VolumeName};
7376
use crate::datasets::{DatasetName, RasterDatasetFromWorkflow, RasterDatasetFromWorkflowResult};
@@ -119,6 +122,7 @@ use utoipa::{Modify, OpenApi};
119122
handlers::datasets::update_loading_info_handler,
120123
handlers::datasets::add_dataset_tiles_handler,
121124
handlers::datasets::get_dataset_tiles_handler,
125+
handlers::datasets::update_dataset_tile_handler,
122126
handlers::layers::add_collection,
123127
handlers::layers::add_existing_collection_to_collection,
124128
handlers::layers::add_existing_layer_to_collection,
@@ -408,6 +412,8 @@ use utoipa::{Modify, OpenApi};
408412
DataPath,
409413
AddDatasetTile,
410414
DatasetTile,
415+
DatasetTileId,
416+
UpdateDatasetTile,
411417
412418
PlotOutputFormat,
413419
WrappedPlotOutput,

services/src/api/handlers/datasets.rs

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ where
9696
web::resource("/{dataset}/provenance")
9797
.route(web::put().to(update_dataset_provenance_handler::<C>)),
9898
)
99+
.service(
100+
web::resource("/{dataset}/tiles/{tile}")
101+
.route(web::put().to(update_dataset_tile_handler::<C>)),
102+
)
99103
.service(
100104
web::resource("/{dataset}/tiles")
101105
.route(web::post().to(add_dataset_tiles_handler::<C>))
@@ -505,6 +509,66 @@ pub async fn get_dataset_tiles_handler<C: ApplicationContext>(
505509
Ok(web::Json(tiles))
506510
}
507511

512+
#[derive(Clone, Serialize, Deserialize, PartialEq, Debug, ToSchema)]
513+
pub struct UpdateDatasetTile {
514+
pub time: crate::api::model::datatypes::TimeInterval,
515+
pub spatial_partition: SpatialPartition2D,
516+
pub band: u32,
517+
pub z_index: u32,
518+
pub params: GdalDatasetParameters,
519+
}
520+
521+
/// Retrieves details about a dataset using the internal name.
522+
#[utoipa::path(
523+
tag = "Datasets",
524+
put,
525+
path = "/dataset/{dataset}/tiles/{tile}",
526+
responses(
527+
(status = 200, description = "OK"),
528+
(status = 401, response = crate::api::model::responses::UnauthorizedUserResponse)
529+
),
530+
params(
531+
("dataset" = DatasetName, description = "Dataset Name"),
532+
("tile" = DatasetTileId, description = "Tile Id"),
533+
),
534+
security(
535+
("session_token" = [])
536+
)
537+
)]
538+
pub async fn update_dataset_tile_handler<C: ApplicationContext>(
539+
dataset: web::Path<(DatasetName, DatasetTileId)>,
540+
session: C::Session,
541+
tile: web::Json<UpdateDatasetTile>,
542+
app_ctx: web::Data<C>,
543+
) -> Result<impl Responder, UpdateDatasetTileError> {
544+
let session_ctx = app_ctx.session_context(session).db();
545+
546+
let (dataset, tile_id) = dataset.into_inner();
547+
548+
let real_dataset = dataset;
549+
550+
let dataset_id = session_ctx
551+
.resolve_dataset_name_to_id(&real_dataset)
552+
.await
553+
.context(CannotLoadDatasetForUpdatingTile)?;
554+
555+
// handle the case where the dataset name is not known
556+
let dataset_id = dataset_id
557+
.ok_or(error::Error::UnknownDatasetName {
558+
dataset_name: real_dataset.to_string(),
559+
})
560+
.context(CannotLoadDatasetForUpdatingTile)?;
561+
562+
// TODO: validate the tile like in add tiles
563+
564+
session_ctx
565+
.update_dataset_tile(dataset_id, tile_id, tile.into_inner())
566+
.await
567+
.context(CannotUpdateDatasetTile)?;
568+
569+
Ok(HttpResponse::Ok())
570+
}
571+
508572
/// Update details about a dataset using the internal name.
509573
#[utoipa::path(
510574
tag = "Datasets",
@@ -5463,4 +5527,140 @@ mod tests {
54635527

54645528
Ok(())
54655529
}
5530+
5531+
#[ge_context::test]
5532+
#[allow(clippy::too_many_lines)]
5533+
async fn it_gets_and_updates_tiles(app_ctx: PostgresContext<NoTls>) -> Result<()> {
5534+
let volume = VolumeName("test_data".to_string());
5535+
5536+
// add data
5537+
let create = CreateDataset {
5538+
data_path: DataPath::Volume(volume.clone()),
5539+
definition: DatasetDefinition {
5540+
properties: AddDataset {
5541+
name: None,
5542+
display_name: "ndvi (tiled)".to_string(),
5543+
description: "ndvi".to_string(),
5544+
source_operator: "MultiBandGdalSource".to_string(),
5545+
symbology: None,
5546+
provenance: None,
5547+
tags: Some(vec!["upload".to_owned(), "test".to_owned()]),
5548+
},
5549+
meta_data: MetaDataDefinition::GdalMultiBand(GdalMultiBand {
5550+
r#type: Default::default(),
5551+
result_descriptor: create_ndvi_result_descriptor(true).into(),
5552+
}),
5553+
},
5554+
};
5555+
5556+
let session = admin_login(&app_ctx).await;
5557+
let ctx = app_ctx.session_context(session.clone());
5558+
5559+
let db = ctx.db();
5560+
5561+
let req = actix_web::test::TestRequest::post()
5562+
.uri("/dataset")
5563+
.append_header((header::CONTENT_LENGTH, 0))
5564+
.append_header((header::AUTHORIZATION, Bearer::new(session.id().to_string())))
5565+
.append_header((header::CONTENT_TYPE, "application/json"))
5566+
.set_payload(serde_json::to_string(&create)?);
5567+
let res = send_test_request(req, app_ctx.clone()).await;
5568+
5569+
let DatasetNameResponse { dataset_name } = actix_web::test::read_body_json(res).await;
5570+
let dataset_id = db
5571+
.resolve_dataset_name_to_id(&dataset_name)
5572+
.await
5573+
.unwrap()
5574+
.unwrap();
5575+
5576+
assert!(db.load_dataset(&dataset_id).await.is_ok());
5577+
5578+
// add tile
5579+
let tiles = create_ndvi_tiles()[0..1].to_vec();
5580+
5581+
let req = actix_web::test::TestRequest::post()
5582+
.uri(&format!("/dataset/{dataset_name}/tiles"))
5583+
.append_header((header::CONTENT_LENGTH, 0))
5584+
.append_header((header::AUTHORIZATION, Bearer::new(session.id().to_string())))
5585+
.append_header((header::CONTENT_TYPE, "application/json"))
5586+
.set_payload(serde_json::to_string(&tiles)?);
5587+
5588+
let res = send_test_request(req, app_ctx.clone()).await;
5589+
assert_eq!(
5590+
res.status(),
5591+
200,
5592+
"response: {read_body}",
5593+
read_body = actix_web::test::read_body_json::<ErrorResponse, _>(res).await
5594+
);
5595+
5596+
// get tile
5597+
let req = actix_web::test::TestRequest::get()
5598+
.uri(&format!("/dataset/{dataset_name}/tiles?offset=0&limit=10"))
5599+
.append_header((header::AUTHORIZATION, Bearer::new(session.id().to_string())));
5600+
5601+
let res = send_test_request(req, app_ctx.clone()).await;
5602+
assert_eq!(
5603+
res.status(),
5604+
200,
5605+
"response: {read_body}",
5606+
read_body = actix_web::test::read_body_json::<ErrorResponse, _>(res).await
5607+
);
5608+
5609+
let returned_tiles: Vec<DatasetTile> = actix_web::test::read_body_json(res).await;
5610+
assert_eq!(returned_tiles.len(), 1);
5611+
assert_eq!(
5612+
returned_tiles[0],
5613+
DatasetTile {
5614+
id: returned_tiles[0].id,
5615+
time: tiles[0].time.clone(),
5616+
spatial_partition: tiles[0].spatial_partition.clone(),
5617+
band: tiles[0].band,
5618+
z_index: tiles[0].z_index,
5619+
params: tiles[0].params.clone()
5620+
}
5621+
);
5622+
5623+
let update_tile = UpdateDatasetTile {
5624+
time: tiles[0].time.clone(),
5625+
spatial_partition: tiles[0].spatial_partition.clone(),
5626+
band: tiles[0].band,
5627+
z_index: tiles[0].z_index + 1,
5628+
params: tiles[0].params.clone(),
5629+
};
5630+
5631+
let req = actix_web::test::TestRequest::put()
5632+
.uri(&format!(
5633+
"/dataset/{dataset_name}/tiles/{}",
5634+
returned_tiles[0].id
5635+
))
5636+
.append_header((header::AUTHORIZATION, Bearer::new(session.id().to_string())))
5637+
.append_header((header::CONTENT_TYPE, "application/json"))
5638+
.set_payload(serde_json::to_string(&update_tile)?);
5639+
5640+
let res = send_test_request(req, app_ctx.clone()).await;
5641+
assert_eq!(res.status(), 200, "response: {res:?}");
5642+
5643+
let req = actix_web::test::TestRequest::get()
5644+
.uri(&format!("/dataset/{dataset_name}/tiles?offset=0&limit=10"))
5645+
.append_header((header::AUTHORIZATION, Bearer::new(session.id().to_string())));
5646+
5647+
let res = send_test_request(req, app_ctx.clone()).await;
5648+
assert_eq!(res.status(), 200, "response: {res:?}");
5649+
5650+
let returned_tiles: Vec<DatasetTile> = actix_web::test::read_body_json(res).await;
5651+
assert_eq!(returned_tiles.len(), 1);
5652+
assert_eq!(
5653+
returned_tiles[0],
5654+
DatasetTile {
5655+
id: returned_tiles[0].id,
5656+
time: tiles[0].time.clone(),
5657+
spatial_partition: tiles[0].spatial_partition.clone(),
5658+
band: tiles[0].band,
5659+
z_index: tiles[0].z_index + 1,
5660+
params: tiles[0].params.clone()
5661+
}
5662+
);
5663+
5664+
Ok(())
5665+
}
54665666
}

services/src/api/model/responses/datasets/errors.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,27 @@ impl fmt::Debug for GetDatasetTilesError {
200200
write!(f, "{}", ge_report(self))
201201
}
202202
}
203+
204+
#[derive(Snafu, IntoStaticStr)]
205+
#[snafu(visibility(pub(crate)))]
206+
#[snafu(context(suffix(false)))] // disables default `Snafu` suffix
207+
pub enum UpdateDatasetTileError {
208+
CannotLoadDatasetForUpdatingTile { source: error::Error },
209+
CannotUpdateDatasetTile { source: error::Error },
210+
}
211+
212+
impl ResponseError for UpdateDatasetTileError {
213+
fn error_response(&self) -> HttpResponse {
214+
HttpResponse::build(self.status_code()).json(ErrorResponse::from(self))
215+
}
216+
217+
fn status_code(&self) -> StatusCode {
218+
StatusCode::BAD_REQUEST
219+
}
220+
}
221+
222+
impl fmt::Debug for UpdateDatasetTileError {
223+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224+
write!(f, "{}", ge_report(self))
225+
}
226+
}

services/src/datasets/postgres.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use std::path::PathBuf;
22

3-
use crate::api::handlers::datasets::{AddDatasetTile, DatasetTile, GetDatasetTilesParams};
3+
use crate::api::handlers::datasets::{
4+
AddDatasetTile, DatasetTile, GetDatasetTilesParams, UpdateDatasetTile,
5+
};
46
use crate::api::model::datatypes::SpatialPartition2D;
57
use crate::api::model::services::{DataPath, UpdateDataset};
6-
use crate::config::Gdal;
78
use crate::contexts::PostgresDb;
89
use crate::datasets::listing::Provenance;
910
use crate::datasets::listing::{DatasetListOptions, DatasetListing, DatasetProvider};
@@ -1370,6 +1371,41 @@ where
13701371

13711372
Ok(tiles)
13721373
}
1374+
1375+
async fn update_dataset_tile(
1376+
&self,
1377+
dataset: DatasetId,
1378+
tile_id: DatasetTileId,
1379+
tile: UpdateDatasetTile,
1380+
) -> Result<()> {
1381+
let mut conn = self.conn_pool.get().await?;
1382+
let tx = conn.build_transaction().start().await?;
1383+
1384+
self.ensure_permission_in_tx(dataset.into(), Permission::Read, &tx)
1385+
.await
1386+
.boxed_context(crate::error::PermissionDb)?;
1387+
1388+
tx.query(
1389+
"
1390+
UPDATE dataset_tiles
1391+
SET dataset_id = $2, time = $3, bbox = $4, band = $5, z_index = $6, gdal_params = $7
1392+
WHERE id = $1;",
1393+
&[
1394+
&tile_id,
1395+
&dataset,
1396+
&tile.time,
1397+
&tile.spatial_partition,
1398+
&tile.band,
1399+
&tile.z_index,
1400+
&(GdalDatasetParameters::from(tile.params)),
1401+
],
1402+
)
1403+
.await?;
1404+
1405+
tx.commit().await?;
1406+
1407+
Ok(())
1408+
}
13731409
}
13741410

13751411
async fn validate_time(

services/src/datasets/storage.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
use super::listing::Provenance;
22
use super::postgres::DatasetMetaData;
33
use super::{DatasetIdAndName, DatasetName};
4-
use crate::api::handlers::datasets::{AddDatasetTile, DatasetTile, GetDatasetTilesParams};
4+
use crate::api::handlers::datasets::{
5+
AddDatasetTile, DatasetTile, GetDatasetTilesParams, UpdateDatasetTile,
6+
};
57
use crate::api::model::services::{DataPath, UpdateDataset};
68
use crate::datasets::listing::{DatasetListing, DatasetProvider};
9+
use crate::datasets::postgres::DatasetTileId;
710
use crate::datasets::upload::UploadDb;
811
use crate::datasets::upload::UploadId;
912
use crate::error::Result;
@@ -329,4 +332,11 @@ pub trait DatasetStore {
329332
dataset: DatasetId,
330333
params: &GetDatasetTilesParams,
331334
) -> Result<Vec<DatasetTile>>;
335+
336+
async fn update_dataset_tile(
337+
&self,
338+
dataset: DatasetId,
339+
tile_id: DatasetTileId,
340+
tile: UpdateDatasetTile,
341+
) -> Result<()>;
332342
}

0 commit comments

Comments
 (0)