Skip to content

Commit 81e4926

Browse files
committed
feat(router): add labels to asset structures
1 parent 2c90857 commit 81e4926

5 files changed

Lines changed: 137 additions & 84 deletions

File tree

backend/server/src/routes/asset/mod.rs

Lines changed: 65 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ use axum::{
1414
extract::{Path, State},
1515
http::StatusCode,
1616
};
17-
use chdrms_database::asset::{self as database, CreateAsset, UpdateAsset};
17+
use chdrms_database::{
18+
asset::{self as database, CreateAsset, UpdateAsset},
19+
label::LabelAndBlocking,
20+
};
1821

1922
use utoipa_axum::{router::OpenApiRouter, routes};
2023
use uuid::Uuid;
@@ -24,7 +27,7 @@ use crate::{
2427
error::{AppError, ErrorResponse, Result},
2528
routes::{
2629
asset::model::{
27-
AssetDto, AssetLocations, CreateAssetRequest, UpdateAssetLocationRequest,
30+
AssetDto, AssetLocations, Block, CreateAssetRequest, UpdateAssetLocationRequest,
2831
UpdateAssetRequest,
2932
},
3033
location::LocationDto,
@@ -64,18 +67,7 @@ async fn get_by_id_or_tag(
6467
}?
6568
.ok_or_else(|| AppError::NotFound)?;
6669

67-
Ok(Json(AssetDto {
68-
id: asset.id,
69-
r#type: asset.r#type,
70-
alias: asset.alias,
71-
notes: asset.notes,
72-
tag: asset.tag,
73-
bundle: asset.bundle,
74-
locations: AssetLocations {
75-
current: asset.location,
76-
home: asset.home_location,
77-
},
78-
}))
70+
Ok(Json(populate_asset_dto(asset, &mut txn).await?))
7971
}
8072

8173
/// Create a new asset.
@@ -120,7 +112,13 @@ async fn create(
120112
alias: asset.alias,
121113
notes: asset.notes,
122114
tag: asset.tag,
115+
// we can assume there are no labels attach to this
116+
// asset as it has just been created.
117+
labels: Vec::new(),
123118
bundle: asset.bundle,
119+
// we can assume there are no blocks on this asset
120+
// as it has just been created.
121+
blocks: Vec::new(),
124122
locations: AssetLocations {
125123
current: asset.location,
126124
home: asset.home_location,
@@ -170,18 +168,7 @@ async fn update(
170168
)
171169
.await?;
172170

173-
Ok(Json(AssetDto {
174-
id: asset.id,
175-
r#type: asset.r#type,
176-
alias: asset.alias,
177-
notes: asset.notes,
178-
tag: asset.tag,
179-
bundle: asset.bundle,
180-
locations: AssetLocations {
181-
current: asset.location,
182-
home: asset.home_location,
183-
},
184-
}))
171+
Ok(Json(populate_asset_dto(asset, &mut txn).await?))
185172
}
186173

187174
/// List all assets.
@@ -199,23 +186,10 @@ async fn list(
199186
State(state): State<AppState>,
200187
_auth: RequirePermission<database::permission::View>,
201188
) -> Result<Json<Vec<AssetDto>>> {
189+
let mut txn = state.transaction().await?;
190+
202191
Ok(Json(
203-
database::Asset::list(&mut state.transaction().await?)
204-
.await?
205-
.into_iter()
206-
.map(|asset| AssetDto {
207-
id: asset.id,
208-
r#type: asset.r#type,
209-
alias: asset.alias,
210-
notes: asset.notes,
211-
tag: asset.tag,
212-
bundle: asset.bundle,
213-
locations: AssetLocations {
214-
current: asset.location,
215-
home: asset.home_location,
216-
},
217-
})
218-
.collect(),
192+
populate_asset_dtos(database::Asset::list(&mut txn).await?, &mut txn).await?,
219193
))
220194
}
221195

@@ -312,20 +286,67 @@ async fn put_location(
312286
.set_location(&mut txn, location)
313287
.await?;
314288

289+
let asset = populate_asset_dto(asset, &mut txn).await?;
290+
315291
txn.commit().await?;
316292

317-
Ok(Json(AssetDto {
293+
Ok(Json(asset))
294+
}
295+
296+
async fn populate_asset_dto(
297+
asset: database::Asset,
298+
txn: &mut sqlx::PgTransaction<'_>,
299+
) -> sqlx::Result<AssetDto> {
300+
let labels =
301+
chdrms_database::label::Label::list_label_and_blocking_for_asset(txn, asset.id).await?;
302+
Ok(populate_asset_dto_raw(asset, labels))
303+
}
304+
305+
pub(super) async fn populate_asset_dtos(
306+
assets: Vec<database::Asset>,
307+
txn: &mut sqlx::PgTransaction<'_>,
308+
) -> sqlx::Result<Vec<AssetDto>> {
309+
// find labels
310+
let labels = chdrms_database::label::Label::list_label_and_blocking_for_assets(
311+
txn,
312+
assets.iter().map(|asset| asset.id).collect(),
313+
)
314+
.await?;
315+
316+
Ok(assets
317+
.into_iter()
318+
.map(|asset| {
319+
let labels = labels.get(&asset.id).cloned().unwrap_or_default();
320+
populate_asset_dto_raw(asset, labels)
321+
})
322+
.collect())
323+
}
324+
325+
fn populate_asset_dto_raw(asset: database::Asset, labels: Vec<LabelAndBlocking>) -> AssetDto {
326+
// construct blocks
327+
let blocks = labels
328+
.iter()
329+
.filter(|label| label.blocking)
330+
.map(|label| Block::Label { label: label.id })
331+
.collect();
332+
333+
// construct labels
334+
let labels = labels.iter().map(|label| label.id).collect();
335+
336+
AssetDto {
318337
id: asset.id,
319338
r#type: asset.r#type,
320339
alias: asset.alias,
321340
notes: asset.notes,
322341
tag: asset.tag,
342+
labels,
323343
bundle: asset.bundle,
344+
blocks,
324345
locations: AssetLocations {
325346
current: asset.location,
326347
home: asset.home_location,
327348
},
328-
}))
349+
}
329350
}
330351

331352
pub(super) fn routes() -> OpenApiRouter<AppState> {

backend/server/src/routes/asset/model.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,17 @@ pub struct AssetDto {
1818
/// The asset tag attached to this asset.
1919
pub tag: String,
2020

21+
/// An array of labels attached to this asset. This field is only
22+
/// mutable via its dedicated field endpoints.
23+
pub labels: Vec<Uuid>,
24+
2125
/// An optional bundle that this asset is within, as asset bundle
2226
/// identifier.
2327
pub bundle: Option<Uuid>,
28+
/// An array of reasons why the asset could be blocked
29+
/// by anything, like a label or maintenance job. This field is
30+
/// immutable and only affected by external fields.
31+
pub blocks: Vec<Block>,
2432

2533
pub locations: AssetLocations,
2634
}
@@ -67,3 +75,14 @@ pub struct AssetLocations {
6775
pub struct UpdateAssetLocations {
6876
pub home: Uuid,
6977
}
78+
79+
#[derive(Debug, Clone, Serialize, ToSchema)]
80+
#[serde(tag = "reason", rename_all = "snake_case")]
81+
pub enum Block {
82+
Label { label: Uuid },
83+
// Assigned {
84+
// project: Uuid,
85+
// until: DateTime<Utc>,
86+
// },
87+
// Maintenance { job: Uuid },
88+
}

backend/server/src/routes/asset_type.rs

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use chdrms_database::{PatchField, asset_type as database, manufacturer::Manufact
1616
use crate::{
1717
auth::{AuthContext, permissions::RequirePermission},
1818
error::{AppError, ErrorResponse, Result},
19-
routes::asset::model::{AssetDto, AssetLocations},
19+
routes::asset::model::AssetDto,
2020
state::AppState,
2121
};
2222

@@ -200,23 +200,11 @@ async fn list_assets_of_asset_type(
200200
.ok_or_else(|| AppError::NotFound)?
201201
.id;
202202

203+
let assets =
204+
chdrms_database::asset::Asset::list_of_type(id, &mut state.transaction().await?).await?;
205+
203206
Ok(Json(
204-
chdrms_database::asset::Asset::list_of_type(id, &mut state.transaction().await?)
205-
.await?
206-
.into_iter()
207-
.map(|asset| AssetDto {
208-
id: asset.id,
209-
r#type: asset.r#type,
210-
alias: asset.alias,
211-
notes: asset.notes,
212-
tag: asset.tag,
213-
bundle: asset.bundle,
214-
locations: AssetLocations {
215-
current: asset.location,
216-
home: asset.home_location,
217-
},
218-
})
219-
.collect(),
207+
super::asset::populate_asset_dtos(assets, &mut txn).await?,
220208
))
221209
}
222210

backend/server/src/routes/bundle/mod.rs

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@ use uuid::Uuid;
1313
use crate::{
1414
auth::{AuthContext, permissions::RequirePermission},
1515
error::{AppError, ErrorResponse, Result},
16-
routes::{
17-
asset::model::{AssetDto, AssetLocations},
18-
bundle::model::AssetBundleDto,
19-
},
16+
routes::{asset::model::AssetDto, bundle::model::AssetBundleDto},
2017
state::AppState,
2118
};
2219

@@ -74,26 +71,14 @@ async fn get_assets_by_id(
7471
) -> Result<Json<Vec<AssetDto>>> {
7572
let mut txn = state.transaction().await?;
7673

74+
let assets = database::Bundle::get_by_id(&mut txn, id)
75+
.await?
76+
.ok_or_else(|| AppError::NotFound)?
77+
.assets(&mut txn)
78+
.await?;
79+
7780
Ok(Json(
78-
database::Bundle::get_by_id(&mut txn, id)
79-
.await?
80-
.ok_or_else(|| AppError::NotFound)?
81-
.assets(&mut txn)
82-
.await?
83-
.into_iter()
84-
.map(|asset| AssetDto {
85-
id: asset.id,
86-
r#type: asset.r#type,
87-
alias: asset.alias,
88-
notes: asset.notes,
89-
tag: asset.tag,
90-
bundle: asset.bundle,
91-
locations: AssetLocations {
92-
current: asset.location,
93-
home: asset.home_location,
94-
},
95-
})
96-
.collect(),
81+
super::asset::populate_asset_dtos(assets, &mut txn).await?,
9782
))
9883
}
9984

openapi/schema.json

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2276,6 +2276,8 @@
22762276
"id",
22772277
"type",
22782278
"tag",
2279+
"labels",
2280+
"blocks",
22792281
"locations"
22802282
],
22812283
"properties": {
@@ -2286,6 +2288,13 @@
22862288
],
22872289
"description": "An optional alias for this asset. This is purely for convenience\nin searching for and identifying multiple instances of similar\nassets."
22882290
},
2291+
"blocks": {
2292+
"type": "array",
2293+
"items": {
2294+
"$ref": "#/components/schemas/Block"
2295+
},
2296+
"description": "An array of reasons why the asset could be blocked\nby anything, like a label or maintenance job. This field is\nimmutable and only affected by external fields."
2297+
},
22892298
"bundle": {
22902299
"type": [
22912300
"string",
@@ -2299,6 +2308,14 @@
22992308
"format": "uuid",
23002309
"description": "The unique identifier for this asset. This property is immutable."
23012310
},
2311+
"labels": {
2312+
"type": "array",
2313+
"items": {
2314+
"type": "string",
2315+
"format": "uuid"
2316+
},
2317+
"description": "An array of labels attached to this asset. This field is only\nmutable via its dedicated field endpoints."
2318+
},
23022319
"locations": {
23032320
"$ref": "#/components/schemas/AssetLocations"
23042321
},
@@ -2410,6 +2427,29 @@
24102427
}
24112428
}
24122429
},
2430+
"Block": {
2431+
"oneOf": [
2432+
{
2433+
"type": "object",
2434+
"required": [
2435+
"label",
2436+
"reason"
2437+
],
2438+
"properties": {
2439+
"label": {
2440+
"type": "string",
2441+
"format": "uuid"
2442+
},
2443+
"reason": {
2444+
"type": "string",
2445+
"enum": [
2446+
"label"
2447+
]
2448+
}
2449+
}
2450+
}
2451+
]
2452+
},
24132453
"ContactDetailsDto": {
24142454
"type": "object",
24152455
"required": [

0 commit comments

Comments
 (0)