WIP: onboard API
Some checks failed
ci/woodpecker/pr/ci Pipeline failed

This commit is contained in:
Daniel Lundin 2024-12-26 21:04:38 +01:00
parent 49372f0e58
commit f351c8615d
Signed by: dln
SSH key fingerprint: SHA256:dQy1Xj3UiqJYpKR5ggQ2bxgz4jCH8IF+k3AB8o0kmdI
6 changed files with 178 additions and 0 deletions

95
Cargo.lock generated
View file

@ -421,6 +421,41 @@ dependencies = [
"typenum",
]
[[package]]
name = "darling"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "debug-ignore"
version = "1.0.5"
@ -1099,6 +1134,12 @@ dependencies = [
"syn",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "1.0.3"
@ -1590,10 +1631,12 @@ dependencies = [
"serde",
"slog",
"slog-async",
"thiserror 2.0.7",
"tokio",
"trace-request",
"tracing",
"tracing-slog",
"validator",
]
[[package]]
@ -1655,6 +1698,28 @@ dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro-error-attr2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "proc-macro-error2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
dependencies = [
"proc-macro-error-attr2",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.92"
@ -3027,6 +3092,36 @@ dependencies = [
"serde",
]
[[package]]
name = "validator"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0b4a29d8709210980a09379f27ee31549b73292c87ab9899beee1c0d3be6303"
dependencies = [
"idna",
"once_cell",
"regex",
"serde",
"serde_derive",
"serde_json",
"url",
"validator_derive",
]
[[package]]
name = "validator_derive"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bac855a2ce6f843beb229757e6e570a42e837bcb15e5f449dd48d5747d41bf77"
dependencies = [
"darling",
"once_cell",
"proc-macro-error2",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "valuable"
version = "0.1.0"

View file

@ -46,6 +46,7 @@ semver = "1.0.24"
serde = { version = "1.0.216", features = ["derive"] }
slog = "2.7.0"
slog-async = "2.8.0"
thiserror = "2"
tokio = { version = "1.42.0", features = ["full"] }
tonic = "0.12.3"
tracing = "0.1.41"
@ -60,3 +61,4 @@ tracing-subscriber = { version = "0.3.19", default-features = false, features =
"fmt",
] }
uuid = { version = "1", features = [ "serde", "v4" ] }
validator = { version = "0.19", features = ["derive"] }

View file

@ -15,10 +15,12 @@ schemars.workspace = true
serde.workspace = true
slog-async.workspace = true
slog.workspace = true
thiserror.workspace = true
tokio.workspace = true
trace-request = { path = "../trace-request" }
tracing-slog.workspace = true
tracing.workspace = true
validator.workspace = true
[package.metadata.cargo-machete]
ignored = ["http"]

View file

@ -4,6 +4,7 @@ use dropshot::ApiDescription;
use std::sync::Arc;
use crate::context::ControllerContext;
use crate::onboard;
use crate::version;
type ControllerApiDescription = ApiDescription<Arc<ControllerContext>>;
@ -11,5 +12,6 @@ type ControllerApiDescription = ApiDescription<Arc<ControllerContext>>;
pub fn api() -> Result<ControllerApiDescription> {
let mut api = ControllerApiDescription::new();
api.register(version::version)?;
api.register(onboard::onboard)?;
Ok(api)
}

View file

@ -1,4 +1,5 @@
pub mod api;
pub mod context;
mod onboard;
mod version;

76
controller/src/onboard.rs Normal file
View file

@ -0,0 +1,76 @@
use dropshot::{endpoint, HttpError, HttpResponseOk, RequestContext, TypedBody};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use trace_request::trace_request;
use validator::Validate;
use std::result::Result;
use std::sync::Arc;
use crate::context::ControllerContext;
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Validate)]
pub struct OnboardRequest {
#[validate(nested)]
ownership_voucher: OwnershipVoucher,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Validate)]
pub struct OwnershipVoucher {
#[validate(length(min = 3, max = 5))]
name: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
pub struct OnboardResponse {
result: String,
}
impl OnboardRequest {
pub fn from(body: TypedBody<OnboardRequest>) -> Result<Self, OnboardError> {
let req = body.into_inner();
req.validate()?;
Ok(req)
}
}
#[derive(Clone, Debug, Error)]
pub enum OnboardError {
#[error("Invalid ownership voucher: {0}")]
ValidationError(#[from] validator::ValidationErrors),
}
impl From<OnboardError> for HttpError {
fn from(e: OnboardError) -> Self {
match e {
OnboardError::ValidationError(msg) => HttpError::for_bad_request(
Some("ValidationError".to_string()),
msg.to_string(),
),
}
}
}
/// Onboard new device
#[endpoint {
method = POST,
path = "/onboard",
}]
#[trace_request]
pub(crate) async fn onboard(
rqctx: RequestContext<Arc<ControllerContext>>,
body: TypedBody<OnboardRequest>,
) -> Result<HttpResponseOk<OnboardResponse>, HttpError> {
tracing::info_span!("Hello, onboard!");
let req = OnboardRequest::from(body)?;
tracing::debug!("Got onboarding request: {:?}", req);
let res = OnboardResponse {
result: "Välkommen ombord!".to_string(),
};
Ok(HttpResponseOk(res))
}