JMAP WIP #5
parent
bfa5bab15d
commit
a548f7509f
|
@ -190,12 +190,10 @@ impl MailBackend for JmapType {
|
|||
let handle = {
|
||||
let tx = w.tx();
|
||||
let closure = move |_work_context| {
|
||||
tx.send(AsyncStatus::Payload(
|
||||
protocol::get_message_list(&connection, &folders.read().unwrap()[&folder_hash])
|
||||
.and_then(|ids| {
|
||||
protocol::get_message(&connection, std::dbg!(&ids).as_slice())
|
||||
}),
|
||||
))
|
||||
tx.send(AsyncStatus::Payload(protocol::get(
|
||||
&connection,
|
||||
&folders.read().unwrap()[&folder_hash],
|
||||
)))
|
||||
.unwrap();
|
||||
tx.send(AsyncStatus::Finished).unwrap();
|
||||
};
|
||||
|
|
|
@ -399,7 +399,6 @@ impl EmailGet {
|
|||
}
|
||||
}
|
||||
|
||||
_impl!(get_call: Get<EmailObject>);
|
||||
_impl!(body_properties: Vec<String>);
|
||||
_impl!(fetch_text_body_values: bool);
|
||||
_impl!(fetch_html_body_values: bool);
|
||||
|
@ -410,8 +409,8 @@ impl EmailGet {
|
|||
#[derive(Serialize, Deserialize, Default, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct EmailFilterCondition {
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub in_mailboxes: Vec<Id>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub in_mailbox: Option<Id>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub in_mailbox_other_than: Vec<Id>,
|
||||
#[serde(skip_serializing_if = "String::is_empty")]
|
||||
|
@ -455,7 +454,11 @@ pub struct EmailFilterCondition {
|
|||
}
|
||||
|
||||
impl EmailFilterCondition {
|
||||
_impl!(in_mailboxes: Vec<Id>);
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
_impl!(in_mailbox: Option<Id>);
|
||||
_impl!(in_mailbox_other_than: Vec<Id>);
|
||||
_impl!(before: UtcDate);
|
||||
_impl!(after: UtcDate);
|
||||
|
|
|
@ -60,7 +60,12 @@ pub struct MailboxGet {
|
|||
#[serde(flatten)]
|
||||
pub get_call: Get<MailboxObject>,
|
||||
}
|
||||
impl MailboxGet {
|
||||
pub fn new(get_call: Get<MailboxObject>) -> Self {
|
||||
MailboxGet { get_call }
|
||||
}
|
||||
}
|
||||
|
||||
impl Method<MailboxObject> for MailboxGet {
|
||||
const NAME: &'static str = "Mailbox/query";
|
||||
const NAME: &'static str = "Mailbox/get";
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ impl Request {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn add_call<M: Method<O>, O: Object>(&mut self, call: M) -> usize {
|
||||
pub fn add_call<M: Method<O>, O: Object>(&mut self, call: &M) -> usize {
|
||||
let seq = get_request_no!(self.request_no);
|
||||
self.method_calls
|
||||
.push(serde_json::to_value((M::NAME, call, &format!("m{}", seq))).unwrap());
|
||||
|
@ -176,10 +176,7 @@ pub struct JsonResponse<'a> {
|
|||
pub fn get_message_list(conn: &JmapConnection, folder: &JmapFolder) -> Result<Vec<String>> {
|
||||
let seq = get_request_no!(conn.request_no);
|
||||
let email_call: EmailQueryCall = EmailQueryCall {
|
||||
filter: EmailFilterCondition {
|
||||
in_mailboxes: vec![folder.id.clone()],
|
||||
..Default::default()
|
||||
},
|
||||
filter: EmailFilterCondition::new().in_mailbox(Some(folder.id.clone())),
|
||||
collapse_threads: false,
|
||||
position: 0,
|
||||
fetch_threads: true,
|
||||
|
@ -201,7 +198,7 @@ pub fn get_message_list(conn: &JmapConnection, folder: &JmapFolder) -> Result<Ve
|
|||
};
|
||||
|
||||
let mut req = Request::new(conn.request_no.clone());
|
||||
req.add_call(email_call);
|
||||
req.add_call(&email_call);
|
||||
|
||||
/*
|
||||
{
|
||||
|
@ -290,12 +287,14 @@ pub fn get_message(conn: &JmapConnection, ids: &[String]) -> Result<Vec<Envelope
|
|||
let seq = get_request_no!(conn.request_no);
|
||||
let email_call: EmailGet = EmailGet::new(
|
||||
Get::new()
|
||||
.ids(Some(ids.iter().cloned().collect::<Vec<String>>()))
|
||||
.ids(Some(JmapArgument::value(
|
||||
ids.iter().cloned().collect::<Vec<String>>(),
|
||||
)))
|
||||
.account_id(conn.account_id.lock().unwrap().clone()),
|
||||
);
|
||||
|
||||
let mut req = Request::new(conn.request_no.clone());
|
||||
req.add_call(email_call);
|
||||
req.add_call(&email_call);
|
||||
let res = conn
|
||||
.client
|
||||
.lock()
|
||||
|
@ -330,3 +329,60 @@ pub fn get_message(conn: &JmapConnection, ids: &[String]) -> Result<Vec<Envelope
|
|||
}))
|
||||
|
||||
*/
|
||||
|
||||
pub fn get(conn: &JmapConnection, folder: &JmapFolder) -> Result<Vec<Envelope>> {
|
||||
let email_query_call: EmailQueryCall = EmailQueryCall {
|
||||
filter: EmailFilterCondition::new().in_mailbox(Some(folder.id.clone())),
|
||||
collapse_threads: false,
|
||||
position: 0,
|
||||
fetch_threads: true,
|
||||
fetch_messages: true,
|
||||
fetch_message_properties: vec![
|
||||
MessageProperty::ThreadId,
|
||||
MessageProperty::MailboxId,
|
||||
MessageProperty::IsUnread,
|
||||
MessageProperty::IsFlagged,
|
||||
MessageProperty::IsAnswered,
|
||||
MessageProperty::IsDraft,
|
||||
MessageProperty::HasAttachment,
|
||||
MessageProperty::From,
|
||||
MessageProperty::To,
|
||||
MessageProperty::Subject,
|
||||
MessageProperty::Date,
|
||||
MessageProperty::Preview,
|
||||
],
|
||||
};
|
||||
|
||||
let mut req = Request::new(conn.request_no.clone());
|
||||
let prev_seq = req.add_call(&email_query_call);
|
||||
|
||||
let email_call: EmailGet = EmailGet::new(
|
||||
Get::new()
|
||||
.ids(Some(JmapArgument::reference(
|
||||
prev_seq,
|
||||
&email_query_call,
|
||||
"/ids",
|
||||
)))
|
||||
.account_id(conn.account_id.lock().unwrap().clone()),
|
||||
);
|
||||
|
||||
req.add_call(&email_call);
|
||||
|
||||
let res = conn
|
||||
.client
|
||||
.lock()
|
||||
.unwrap()
|
||||
.post("https://jmap-proxy.local/jmap/fc32dffe-14e7-11ea-a277-2477037a1804/")
|
||||
.json(&req)
|
||||
.send();
|
||||
|
||||
let res_text = res?.text()?;
|
||||
|
||||
let mut v: MethodResponse = serde_json::from_str(&res_text).unwrap();
|
||||
let e = GetResponse::<EmailObject>::try_from(v.method_responses.pop().unwrap())?;
|
||||
let GetResponse::<EmailObject> { list, .. } = e;
|
||||
Ok(list
|
||||
.into_iter()
|
||||
.map(std::convert::Into::into)
|
||||
.collect::<Vec<Envelope>>())
|
||||
}
|
||||
|
|
|
@ -21,13 +21,16 @@
|
|||
|
||||
use super::Id;
|
||||
use core::marker::PhantomData;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::ser::{Serialize, SerializeStruct, Serializer};
|
||||
use serde_json::{value::RawValue, Value};
|
||||
|
||||
mod filters;
|
||||
pub use filters::*;
|
||||
mod comparator;
|
||||
pub use comparator::*;
|
||||
mod argument;
|
||||
pub use argument::*;
|
||||
|
||||
use super::protocol::{Method, Response};
|
||||
use std::collections::HashMap;
|
||||
|
@ -102,7 +105,7 @@ pub struct Account {
|
|||
extra_properties: HashMap<String, Value>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Get<OBJ: Object>
|
||||
where
|
||||
|
@ -111,7 +114,8 @@ where
|
|||
#[serde(skip_serializing_if = "String::is_empty")]
|
||||
pub account_id: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub ids: Option<Vec<String>>,
|
||||
#[serde(flatten)]
|
||||
pub ids: Option<JmapArgument<Vec<String>>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub properties: Option<Vec<String>>,
|
||||
#[serde(skip)]
|
||||
|
@ -131,9 +135,65 @@ where
|
|||
}
|
||||
}
|
||||
_impl!(account_id: String);
|
||||
_impl!(ids: Option<Vec<String>>);
|
||||
_impl!(ids: Option<JmapArgument<Vec<String>>>);
|
||||
_impl!(properties: Option<Vec<String>>);
|
||||
}
|
||||
|
||||
impl<OBJ: Object + Serialize + std::fmt::Debug> Serialize for Get<OBJ> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut fields_no = 0;
|
||||
if !self.account_id.is_empty() {
|
||||
fields_no += 1;
|
||||
}
|
||||
if !self.ids.is_none() {
|
||||
fields_no += 1;
|
||||
}
|
||||
if !self.properties.is_none() {
|
||||
fields_no += 1;
|
||||
}
|
||||
|
||||
let mut state = serializer.serialize_struct("Get", fields_no)?;
|
||||
if !self.account_id.is_empty() {
|
||||
state.serialize_field("accountId", &self.account_id)?;
|
||||
}
|
||||
match self.ids.as_ref() {
|
||||
None => {}
|
||||
Some(JmapArgument::Value(ref v)) => state.serialize_field("ids", v)?,
|
||||
Some(JmapArgument::ResultReference {
|
||||
ref result_of,
|
||||
ref name,
|
||||
ref path,
|
||||
}) => {
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct A<'a> {
|
||||
result_of: &'a str,
|
||||
name: &'a str,
|
||||
path: &'a str,
|
||||
}
|
||||
|
||||
state.serialize_field(
|
||||
"#ids",
|
||||
&A {
|
||||
result_of,
|
||||
name,
|
||||
path,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
if !self.properties.is_none() {
|
||||
state.serialize_field("properties", self.properties.as_ref().unwrap());
|
||||
}
|
||||
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
// The response has the following arguments:
|
||||
//
|
||||
// o accountId: "Id"
|
||||
|
@ -192,6 +252,7 @@ pub struct MethodResponse<'a> {
|
|||
pub struct GetResponse<OBJ: Object> {
|
||||
#[serde(skip_serializing_if = "String::is_empty")]
|
||||
pub account_id: String,
|
||||
#[serde(default)]
|
||||
pub state: String,
|
||||
pub list: Vec<OBJ>,
|
||||
pub not_found: Vec<String>,
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* meli - jmap module.
|
||||
*
|
||||
* Copyright 2019 Manos Pitsidianakis
|
||||
*
|
||||
* This file is part of meli.
|
||||
*
|
||||
* meli is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* meli is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use crate::backends::jmap::protocol::Method;
|
||||
use crate::backends::jmap::rfc8620::Object;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::ser::{Serialize, SerializeStruct, Serializer};
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum JmapArgument<T> {
|
||||
Value(T),
|
||||
ResultReference {
|
||||
result_of: String,
|
||||
name: String,
|
||||
path: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl<T> JmapArgument<T> {
|
||||
pub fn value(v: T) -> Self {
|
||||
JmapArgument::Value(v)
|
||||
}
|
||||
|
||||
pub fn reference<M, OBJ>(result_of: usize, method: &M, path: &str) -> Self
|
||||
where
|
||||
M: Method<OBJ>,
|
||||
OBJ: Object,
|
||||
{
|
||||
JmapArgument::ResultReference {
|
||||
result_of: format!("m{}", result_of),
|
||||
name: M::NAME.to_string(),
|
||||
path: path.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue