credibil_dwn/endpoint.rs
1//! # Endpoint
2//!
3//! `Endpoint` provides the entry point for DWN messages. Messages are routed
4//! to the appropriate handler for processing, returning a reply that can be
5//! serialized to a JSON object.
6
7use std::fmt::Debug;
8
9use serde::{Deserialize, Serialize};
10
11use crate::authorization::Authorization;
12use crate::interfaces::Descriptor;
13use crate::provider::Provider;
14use crate::{Result, schema, unauthorized};
15
16/// Handle incoming messages.
17///
18/// # Errors
19///
20/// This method can fail for a number of reasons related to the imcoming
21/// message's viability. Expected failues include invalid authorization,
22/// insufficient permissions, and invalid message content.
23///
24/// Implementers should look to the Error type and description for more
25/// information on the reason for failure.
26pub async fn handle<T>(
27 owner: &str, message: impl Message<Reply = T>, provider: &impl Provider,
28) -> Result<Reply<T>> {
29 message.validate(owner, provider).await?;
30 message.handle(owner, provider).await
31}
32
33/// Methods common to all messages.
34///
35/// The primary role of this trait is to provide a common interface for
36/// messages so they can be handled by [`handle`] method.
37pub trait Message: Serialize + Clone + Debug + Send + Sync {
38 /// The inner reply type specific to the implementing message.
39 type Reply;
40
41 /// Returns message descriptor properties common to all messages (i.e.,
42 /// `interface`, `method`, and `message_timestamp`).
43 fn descriptor(&self) -> &Descriptor;
44
45 /// Returns the messages's authorization, when set.
46 fn authorization(&self) -> Option<&Authorization>;
47
48 /// Routes the message to the concrete handler used to process the message.
49 fn handle(
50 self, owner: &str, provider: &impl Provider,
51 ) -> impl Future<Output = Result<Reply<Self::Reply>>> + Send;
52
53 /// Perform initial validation of the message.
54 ///
55 /// Validation undertaken here is common to all messages, with message-
56 /// specific validation performed by the message's handler.
57 fn validate(
58 &self, _owner: &str, provider: &impl Provider,
59 ) -> impl Future<Output = Result<()>> + Send {
60 async {
61 // if !tenant_gate.active(owner)? {
62 // return Err(Error::Unauthorized("tenant not active"));
63 // }
64
65 // validate the message schema during development
66 #[cfg(debug_assertions)]
67 schema::validate(self)?;
68
69 // authenticate the requestor
70 if let Some(authzn) = self.authorization() {
71 if let Err(e) = authzn.verify(provider.clone()).await {
72 return Err(unauthorized!("failed to authenticate: {e}"));
73 }
74 }
75
76 Ok(())
77 }
78 }
79}
80
81/// Top-level reply data structure common to all handler.
82#[derive(Debug, Default, Deserialize, Serialize)]
83pub struct Reply<ReplyBody> {
84 /// The status message to accompany the reply.
85 pub status: Status,
86
87 /// The endpoint-specific reply.
88 #[serde(skip_serializing_if = "Option::is_none")]
89 #[serde(flatten)]
90 pub body: Option<ReplyBody>,
91}
92
93/// Reply status.
94#[derive(Clone, Debug, Default, Deserialize, Serialize)]
95#[serde(rename_all = "camelCase")]
96pub struct Status {
97 /// Status code.
98 pub code: u16,
99
100 /// Status detail.
101 #[serde(skip_serializing_if = "Option::is_none")]
102 pub detail: Option<String>,
103}
104
105// impl Message for Subscribe {
106// type Reply = SubscribeReply;
107
108// fn descriptor(&self) -> &Descriptor {
109// &self.descriptor.base
110// }
111
112// fn authorization(&self) -> Option<&Authorization> {
113// Some(&self.authorization)
114// }
115
116// async fn handle(self, owner: &str, provider: &impl Provider) -> Result<Reply<Self::Reply>> {
117// messages::subscribe::handle(owner, self, provider).await
118// }
119// }