drawbridge_server/trees/
put.rs

1// SPDX-FileCopyrightText: 2022 Profian Inc. <opensource@profian.com>
2// SPDX-License-Identifier: Apache-2.0
3
4use super::super::{OidcClaims, ScopeContext, ScopeLevel, Store};
5
6use drawbridge_type::{Meta, TreeContext, TreeDirectory};
7
8use async_std::sync::Arc;
9use axum::body::Body;
10use axum::extract::{BodyStream, RequestParts};
11use axum::http::{Request, StatusCode};
12use axum::response::IntoResponse;
13use axum::{Extension, Json};
14use futures::{io, TryStreamExt};
15use tracing::{debug, trace};
16
17pub async fn put(
18    Extension(ref store): Extension<Arc<Store>>,
19    claims: OidcClaims,
20    cx: TreeContext,
21    meta: Meta,
22    req: Request<Body>,
23) -> impl IntoResponse {
24    trace!(target: "app::trees::put", "called for `{cx}`");
25
26    if meta.hash.is_empty() {
27        return Err((
28            StatusCode::BAD_REQUEST,
29            "At least one content digest value must be specified",
30        )
31            .into_response());
32    }
33
34    let user = claims
35        .assert_user(
36            store,
37            &cx.tag.repository.owner,
38            ScopeContext::Tag,
39            ScopeLevel::Write,
40        )
41        .await
42        .map_err(IntoResponse::into_response)?;
43
44    let mut req = RequestParts::new(req);
45    let tag = user.repository(&cx.tag.repository.name).tag(&cx.tag.name);
46    match meta.mime.to_string().as_str() {
47        TreeDirectory::<()>::TYPE => {
48            let dir = req
49                .extract()
50                .await
51                .map(|Json(v)| v)
52                .map_err(|e| (StatusCode::BAD_REQUEST, e).into_response())?;
53            tag.create_directory_node(&cx.path, meta, &dir).await
54        }
55        _ => {
56            let body = req
57                .extract::<BodyStream>()
58                .await
59                .map_err(|e| (StatusCode::BAD_REQUEST, e).into_response())?
60                .map_err(|e| io::Error::new(io::ErrorKind::Other, e));
61            tag.create_file_node(&cx.path, meta, body.into_async_read())
62                .await
63        }
64    }
65    .map_err(|e| {
66        debug!(target: "app::trees::put", "failed for `{cx}`: {:?}", e);
67        e.into_response()
68    })
69    .map(|_| StatusCode::CREATED)
70}