use bytes::Bytes;
use reqwest::Method;
use serde::{Deserialize, Serialize};
use crate::error::Result;
use crate::http::HttpClient;
use crate::models::{ArtifactName, Document, SignDocumentItem, SignerSelf, SignerType};
use crate::pagination::Page;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerifyCodeBody {
#[serde(rename = "verification-code")]
pub code: String,
#[serde(rename = "signer-access-code", skip_serializing_if = "Option::is_none")]
pub signer_access_code: Option<String>,
}
impl VerifyCodeBody {
pub fn new<S: Into<String>>(code: S) -> Self {
Self {
code: code.into(),
signer_access_code: None,
}
}
pub fn access_code<S: Into<String>>(mut self, code: S) -> Self {
self.signer_access_code = Some(code.into());
self
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ConfirmSignerDataBody {
#[serde(skip_serializing_if = "Option::is_none")]
pub full_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub whatsapp_phone_number: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub has_accepted_terms: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub code: Option<String>,
}
impl ConfirmSignerDataBody {
pub fn new() -> Self {
Self::default()
}
pub fn email<S: Into<String>>(mut self, email: S) -> Self {
self.email = Some(email.into());
self
}
pub fn full_name<S: Into<String>>(mut self, full_name: S) -> Self {
self.full_name = Some(full_name.into());
self
}
pub fn whatsapp<S: Into<String>>(mut self, phone: S) -> Self {
self.whatsapp_phone_number = Some(phone.into());
self
}
pub fn accepted_terms(mut self, accepted: bool) -> Self {
self.has_accepted_terms = Some(accepted);
self
}
pub fn verification_code<S: Into<String>>(mut self, code: S) -> Self {
self.code = Some(code.into());
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SignMultipleDocumentsBody {
pub document_ids: Vec<String>,
}
impl SignMultipleDocumentsBody {
pub fn new<I, S>(document_ids: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
Self {
document_ids: document_ids.into_iter().map(Into::into).collect(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeclineMultipleDocumentsBody {
pub document_ids: Vec<String>,
pub decline_reason: String,
}
impl DeclineMultipleDocumentsBody {
pub fn new<I, S, R>(document_ids: I, reason: R) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
R: Into<String>,
{
Self {
document_ids: document_ids.into_iter().map(Into::into).collect(),
decline_reason: reason.into(),
}
}
}
#[derive(Debug)]
pub struct ListSignerDocumentsRequest<'a> {
http: &'a HttpClient,
signer_id: String,
page: Option<u32>,
per_page: Option<u32>,
status: Option<String>,
method: Option<String>,
search: Option<String>,
sort: Option<String>,
}
impl<'a> ListSignerDocumentsRequest<'a> {
pub fn page(mut self, page: u32) -> Self {
self.page = Some(page);
self
}
pub fn per_page(mut self, per_page: u32) -> Self {
self.per_page = Some(per_page);
self
}
pub fn status<S: Into<String>>(mut self, status: S) -> Self {
self.status = Some(status.into());
self
}
pub fn method<S: Into<String>>(mut self, method: S) -> Self {
self.method = Some(method.into());
self
}
pub fn search<S: Into<String>>(mut self, search: S) -> Self {
self.search = Some(search.into());
self
}
pub fn sort<S: Into<String>>(mut self, sort: S) -> Self {
self.sort = Some(sort.into());
self
}
pub async fn send(self) -> Result<Page<Document>> {
let path = format!("signers/{}/documents", self.signer_id);
let mut req = self.http.request(Method::GET, &path)?;
let mut q: Vec<(&str, String)> = Vec::new();
if let Some(v) = self.page {
q.push(("page", v.to_string()));
}
if let Some(v) = self.per_page {
q.push(("per-page", v.to_string()));
}
if let Some(v) = self.status {
q.push(("status", v));
}
if let Some(v) = self.method {
q.push(("method", v));
}
if let Some(v) = self.search {
q.push(("search", v));
}
if let Some(v) = self.sort {
q.push(("sort", v));
}
if !q.is_empty() {
req = req.query(&q);
}
self.http.send_paged(req).await
}
}
#[derive(Debug)]
pub struct SignerSelfApi<'a> {
http: &'a HttpClient,
}
impl<'a> SignerSelfApi<'a> {
pub(crate) fn new(http: &'a HttpClient) -> Self {
Self { http }
}
pub async fn me(&self) -> Result<SignerSelf> {
let req = self.http.request(Method::GET, "signers/self")?;
self.http.send_envelope(req).await
}
pub async fn accept_terms(&self) -> Result<()> {
let mut req = self.http.request(Method::PUT, "signers/accept-terms")?;
if let Some(code) = self.http.auth().signer_access_code() {
req = req.json(&serde_json::json!({ "signer-access-code": code }));
}
self.http.send_no_content(req).await
}
pub async fn verify(&self, body: &VerifyCodeBody) -> Result<()> {
let mut json = serde_json::to_value(body)?;
if let (Some(code), Some(object)) =
(self.http.auth().signer_access_code(), json.as_object_mut())
{
object
.entry("signer-access-code")
.or_insert_with(|| serde_json::Value::String(code.to_owned()));
}
let req = self.http.request(Method::POST, "verify")?.json(&json);
self.http.send_no_content(req).await
}
pub async fn confirm_data<S: AsRef<str>>(
&self,
document_id: S,
body: &ConfirmSignerDataBody,
) -> Result<()> {
let path = format!("documents/{}/signers/confirm-data", document_id.as_ref());
let req = self.http.request(Method::PUT, &path)?.json(body);
self.http.send_no_content(req).await
}
pub async fn signable_document(&self) -> Result<Document> {
let req = self.http.request(Method::GET, "sign")?;
self.http.send_data(req).await
}
pub async fn sign<D: AsRef<str>, A: AsRef<str>, I>(
&self,
document_id: D,
assignment_id: A,
items: I,
) -> Result<()>
where
I: IntoIterator<Item = SignDocumentItem>,
{
let path = format!(
"documents/{}/assignments/{}",
document_id.as_ref(),
assignment_id.as_ref()
);
let items: Vec<SignDocumentItem> = items.into_iter().collect();
let req = self.http.request(Method::POST, &path)?.json(&items);
self.http.send_no_content(req).await
}
pub async fn decline<D: AsRef<str>, A: AsRef<str>, R: AsRef<str>>(
&self,
document_id: D,
assignment_id: A,
reason: R,
) -> Result<()> {
let path = format!(
"documents/{}/assignments/{}/reject",
document_id.as_ref(),
assignment_id.as_ref()
);
let req = self
.http
.request(Method::PUT, &path)?
.json(&serde_json::json!({ "decline_reason": reason.as_ref() }));
self.http.send_no_content(req).await
}
pub async fn current_document<S: AsRef<str>>(&self, signer_id: S) -> Result<Document> {
let path = format!("signers/{}/document", signer_id.as_ref());
let req = self.http.request(Method::GET, &path)?;
self.http.send_envelope(req).await
}
pub fn list_documents<S: Into<String>>(&self, signer_id: S) -> ListSignerDocumentsRequest<'_> {
ListSignerDocumentsRequest {
http: self.http,
signer_id: signer_id.into(),
page: None,
per_page: None,
status: None,
method: None,
search: None,
sort: None,
}
}
pub async fn sign_multiple(&self, body: &SignMultipleDocumentsBody) -> Result<()> {
let req = self
.http
.request(Method::PUT, "signers/documents/sign-multiple")?
.json(body);
self.http.send_no_content(req).await
}
pub async fn decline_multiple(&self, body: &DeclineMultipleDocumentsBody) -> Result<()> {
let req = self
.http
.request(Method::PUT, "signers/documents/decline-multiple")?
.json(body);
self.http.send_no_content(req).await
}
pub async fn download_document<S: AsRef<str>, D: AsRef<str>>(
&self,
signer_id: S,
document_id: D,
artifact: impl Into<ArtifactName>,
) -> Result<(Bytes, String)> {
let artifact: ArtifactName = artifact.into();
let path = format!(
"signers/{}/documents/{}/download/{}",
signer_id.as_ref(),
document_id.as_ref(),
artifact.as_str()
);
let req = self.http.request(Method::GET, &path)?;
let (bytes, headers) = self.http.send_bytes(req).await?;
let content_type = headers
.get(reqwest::header::CONTENT_TYPE)
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream")
.to_owned();
Ok((bytes, content_type))
}
pub async fn upload_signature(
&self,
kind: SignerType,
content_type: &str,
bytes: impl Into<Bytes>,
) -> Result<()> {
let bytes = bytes.into();
let req = self
.http
.request(Method::POST, "signature")?
.query(&[("type", kind.as_str())])
.header(reqwest::header::CONTENT_TYPE, content_type)
.body(bytes);
self.http.send_no_content(req).await
}
pub async fn download_signature(&self, kind: SignerType) -> Result<(Bytes, String)> {
let path = format!("signature/{}", kind.as_str());
let req = self.http.request(Method::GET, &path)?;
let (bytes, headers) = self.http.send_bytes(req).await?;
let content_type = headers
.get(reqwest::header::CONTENT_TYPE)
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream")
.to_owned();
Ok((bytes, content_type))
}
}