use content_addressable::{canonical, ContentAddressable, ContentError};
use serde::{Deserialize, Serialize};
use crate::error::ProtocolError;
use crate::ids::{
ControlId, DefinitionId, IdempotencyKey, InstanceId, OptionId, ResponseId, Revision,
};
use crate::instance::Audience;
pub const RESPONSE_SCHEMA_V1: &str = "newt.interaction.response/v1";
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(into = "String", try_from = "String")]
pub struct SecretRef(String);
#[cfg(feature = "schema")]
crate::string_scalar_schema!(SecretRef, None::<&str>);
impl From<SecretRef> for String {
fn from(value: SecretRef) -> Self {
value.0
}
}
impl TryFrom<String> for SecretRef {
type Error = ProtocolError;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::new(value)
}
}
impl SecretRef {
pub fn new(handle: impl Into<String>) -> Result<Self, ProtocolError> {
let handle = handle.into();
if handle.is_empty() {
return Err(ProtocolError::InvalidId {
kind: "secret reference",
reason: "must not be empty".to_string(),
});
}
Ok(Self(handle))
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "kebab-case", deny_unknown_fields)]
pub enum ControlValue {
Choice {
option: OptionId,
},
Text {
text: String,
},
Toggle {
on: bool,
},
Secret {
reference: SecretRef,
},
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ResponderProvenance {
pub kind: AssertionKind,
pub subject: String,
pub audience: Audience,
pub assertion: Option<String>,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum AssertionKind {
TerminalOperator,
SignedAssertion,
Unauthenticated,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Submission {
pub control: ControlId,
pub value: ControlValue,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Response {
pub schema: crate::tag::ResponseTag,
pub definition: DefinitionId,
pub instance: InstanceId,
pub revision: Revision,
pub values: Vec<Submission>,
pub idempotency_key: IdempotencyKey,
pub responder_provenance: ResponderProvenance,
}
impl Response {
pub fn response_id(&self) -> Result<ResponseId, ProtocolError> {
Ok(ResponseId::from_content_id(self.content_id()?))
}
#[must_use]
pub fn schema_tag(&self) -> &'static str {
self.schema.as_str()
}
}
impl ContentAddressable for Response {
fn canonical_form(&self) -> Result<Vec<u8>, ContentError> {
canonical::to_canonical_dagcbor(self)
}
}