fundamentum_edge_mcu_http_client/
provisioning_api.rs

1use core::marker::PhantomData;
2
3use crate::{
4    models::{DeviceFQN, Empty, HttpMethod},
5    HttpClient, HttpClientErrorWrapper, HttpHandler,
6};
7
8use super::{
9    api_version::{ApiVersion, V3},
10    models::{DeviceIdentity, ProvisionRequestV3, ProvisionResponse},
11};
12
13const PROVISIONING_PATH: &str = "/provision";
14
15/// Provisioning section of the devices API. More information in the [documentation][provision-doc] and [swagger].
16///
17/// [provision-doc]: https://dimonoff.atlassian.net/wiki/spaces/F2/pages/306273353781/Devices+API
18/// [swagger]: https://devices.fundamentum-iot.com/api/v3/docs/#/Provisioning
19pub struct ProvisioningApi<'a, V: ApiVersion, H: HttpHandler> {
20    http_client: &'a HttpClient<'a, V, H>,
21
22    // We use PhantomData to make the compiler think that we are using the V type.
23    // Read more here: <https://doc.rust-lang.org/nomicon/phantom-data.html#table-of-phantomdata-patterns>
24    _marker: PhantomData<fn() -> V>,
25}
26
27impl<'a, V: ApiVersion, H: HttpHandler> ProvisioningApi<'a, V, H> {
28    /// Create a new interface for the Provisioning API.
29    #[must_use]
30    pub const fn new(http_client: &'a HttpClient<'a, V, H>) -> Self {
31        Self {
32            http_client,
33            _marker: PhantomData,
34        }
35    }
36}
37
38impl<'a, H: HttpHandler> ProvisioningApi<'a, V3, H> {
39    /// Provision a new device using the [`V3`] API.
40    ///
41    /// # Errors
42    /// An error may occur while sending the request or by the API
43    pub async fn provision<'d>(
44        &'a self,
45        device_fqn: &'a DeviceFQN<'a>,
46        asset_type_id: i32,
47        access_token: &'a str,
48        device_identity: &'a DeviceIdentity<'a>,
49        mode: ProvisionMode,
50        out_buffer: &'d mut [u8],
51    ) -> Result<ProvisionResponse<'d, Empty>, HttpClientErrorWrapper<'d, H>> {
52        let body = ProvisionRequestV3 {
53            project_id: device_fqn.registry_fqn.project_id,
54            region_id: device_fqn.registry_fqn.region_id,
55            registry_id: device_fqn.registry_fqn.registry_id,
56            serial_number: device_fqn.serial_number,
57            asset_type_id,
58            access_token,
59            algorithm: &device_identity.tag(),
60            secret: match *device_identity {
61                DeviceIdentity::Rsa256 { public_key } => public_key,
62            },
63        };
64
65        self.http_client
66            .send(mode.into(), None, PROVISIONING_PATH, body, out_buffer)
67            .await
68    }
69}
70
71/// Indicates the provisioning mode.
72#[derive(Default)]
73pub enum ProvisionMode {
74    /// Provision a device normally.
75    #[default]
76    Normal,
77    /// Force the provisioning of a device and override the previous device.
78    Force,
79}
80
81#[allow(clippy::from_over_into)]
82impl Into<HttpMethod> for ProvisionMode {
83    fn into(self) -> HttpMethod {
84        match self {
85            Self::Normal => HttpMethod::POST,
86            Self::Force => HttpMethod::PUT,
87        }
88    }
89}