From 21a5c705879bf3b237e74e55a3a433414a191876 Mon Sep 17 00:00:00 2001 From: Lyndon Maydwell Date: Thu, 21 Sep 2023 20:09:01 +1000 Subject: [PATCH] updating sendgrid connector to have simplified inputs to work with V3-Engine --- crates/ndc-sendgrid/src/mutation.rs | 93 ++++++++++++++++++++++++- crates/ndc-sendgrid/src/query.rs | 21 ------ crates/ndc-sendgrid/src/schema.rs | 73 +++++++++++++++++-- crates/ndc-sendgrid/src/sendgrid_api.rs | 20 +++++- 4 files changed, 175 insertions(+), 32 deletions(-) diff --git a/crates/ndc-sendgrid/src/mutation.rs b/crates/ndc-sendgrid/src/mutation.rs index 9b6e341..764ff87 100644 --- a/crates/ndc-sendgrid/src/mutation.rs +++ b/crates/ndc-sendgrid/src/mutation.rs @@ -8,6 +8,7 @@ use serde_json::Value; use std::collections::BTreeMap; use crate::schema::SEND_MAIL; +use crate::sendgrid_api::{SimpleSendMailRequest, MailAddress, MailContent, MailPersonalization}; use super::configuration; use super::schema; @@ -58,13 +59,44 @@ async fn process_operation( } } +fn complicate_request(simple_request: SimpleSendMailRequest) -> sendgrid_api::SendMailRequest { + + let personalization = MailPersonalization { + from: Some(simple_request.from.clone()), + to: vec!(simple_request.to), + cc: simple_request.cc.map(|x| vec!(x)), + bcc: simple_request.bcc.map(|x| vec!(x)), + subject: Some(simple_request.subject.clone()), + headers: None, + substitutions: None, + dynamic_template_data: None, + send_at: simple_request.send_at + }; + + sendgrid_api::SendMailRequest { + personalizations: vec!(personalization), + from: simple_request.from, + reply_to_list: vec!(), + subject: simple_request.subject, + content: vec!(simple_request.content), + attachments: simple_request.attachment.map(|a| vec!(a)), + template_id: simple_request.template_id, + headers: None, + send_at: simple_request.send_at, + batch_id: simple_request.batch_id, + asm: simple_request.asm + } +} + async fn process_send_mail( http_client: &reqwest::Client, configuration: &configuration::SendGridConfiguration, arguments: BTreeMap, fields: Option>, ) -> Result { - let request = parse_send_mail_args(&arguments)?; + let simple_request = parse_simple_send_mail_args(&arguments)?; + let request = complicate_request(simple_request); + sendgrid_api::invoke_send_mail(http_client, &configuration.sendgrid_api_key, &request) .await .map_err(|err| MutationError::Other(Box::new(err)))?; @@ -105,7 +137,64 @@ async fn process_send_mail( }) } -fn parse_send_mail_args( +fn invalid_arg(err: &str) -> MutationError { + MutationError::InvalidRequest(format!("Couldn't find '{err}' field in arguments")) +} + +fn invalid_deserialize(arg: &str, err: serde_json::Error) -> MutationError { + MutationError::InvalidRequest(format!("Unable to deserialize argument '{arg}': {err}")) +} + +fn parse_simple_send_mail_args( + in_args: &BTreeMap, +) -> Result { + + let args_to = in_args.get("to").ok_or(invalid_arg("request"))?; + let args_cc = in_args.get("cc"); + let args_bcc = in_args.get("bcc"); + let args_from = in_args.get("from").ok_or(invalid_arg("from"))?; + let args_reply_to = in_args.get("reply_to"); + let args_subject = in_args.get("subject").ok_or(invalid_arg("subject"))?; + let args_content = in_args.get("content").ok_or(invalid_arg("content"))?; + let args_content_type = in_args.get("content_type").ok_or(invalid_arg("content_type"))?; + let args_attachment = in_args.get("attachment"); + let args_template_id = in_args.get("template_id"); + let args_send_at = in_args.get("send_at"); + let args_batch_id = in_args.get("batch_id"); + let args_asm = in_args.get("asm"); + + let to = serde_json::from_value(args_to.clone()).map_err(|err| invalid_deserialize("to", err))?; + let cc = args_cc.map(|x| serde_json::from_value(x.clone()).map_err(|err| invalid_deserialize("cc", err))).unwrap_or(Ok(None))?; + let bcc = args_bcc.map(|x| serde_json::from_value(x.clone()).map_err(|err| invalid_deserialize("bcc", err))).unwrap_or(Ok(None))?; + let from = serde_json::from_value(args_from.clone()).map_err(|err| invalid_deserialize("from", err))?; + let reply_to = args_reply_to.map(|x| serde_json::from_value(x.clone()).map_err(|err| invalid_deserialize("reply_to", err))).unwrap_or(Ok(None))?; + let subject = serde_json::from_value(args_subject.clone()).map_err(|err| invalid_deserialize("subject", err))?; + let content = serde_json::from_value(args_content.clone()).map_err(|err| invalid_deserialize("content", err))?; + let content_type = serde_json::from_value(args_content_type.clone()).map_err(|err| invalid_deserialize("content", err))?; + let attachment = args_attachment.map(|x| serde_json::from_value(x.clone()).map_err(|err| invalid_deserialize("attachment", err))).unwrap_or(Ok(None))?; + let template_id = args_template_id.map(|x| serde_json::from_value(x.clone()).map_err(|err| invalid_deserialize("template_id", err))).unwrap_or(Ok(None))?; + let send_at = args_send_at.map(|x| serde_json::from_value(x.clone()).map_err(|err| invalid_deserialize("send_at", err))).unwrap_or(Ok(None))?; + let batch_id = args_batch_id.map(|x| serde_json::from_value(x.clone()).map_err(|err| invalid_deserialize("batch_id", err))).unwrap_or(Ok(None))?; + let asm = args_asm.map(|x| serde_json::from_value(x.clone()).map_err(|err| invalid_deserialize("asm", err))).unwrap_or(Ok(None))?; + + let request = sendgrid_api::SimpleSendMailRequest { + to: MailAddress { email: to, name: None }, + cc: cc.map(|x| MailAddress { email: x, name: None }), + bcc: bcc.map(|x| MailAddress { email: x, name: None }), + from: MailAddress { email: from, name: None }, + reply_to: reply_to.map(|x| MailAddress { email: x, name: None }), + subject, + content: MailContent { r#type: content_type, value: content }, + attachment, + template_id, + send_at, + batch_id, + asm, + }; + Ok(request) +} + +fn _parse_send_mail_args( in_args: &BTreeMap, ) -> Result { let args_request = diff --git a/crates/ndc-sendgrid/src/query.rs b/crates/ndc-sendgrid/src/query.rs index b276798..e977d89 100644 --- a/crates/ndc-sendgrid/src/query.rs +++ b/crates/ndc-sendgrid/src/query.rs @@ -9,27 +9,6 @@ use super::configuration; use super::schema::LIST_TEMPLATES_FUNCTION_NAME; use super::sendgrid_api::{invoke_list_function_templates, ListTransactionalTemplatesParams}; - - -// String::from("generations"), -// ArgumentInfo { -// description: Some(String::from("Comma-delimited list specifying which generations of templates to return. Options are legacy, dynamic or legacy,dynamic")), -// argument_type: nullable(named("String")) -// }), -// ( -// String::from("page_size"), -// ArgumentInfo { -// description: Some(String::from("The number of templates to be returned in each page of results")), -// argument_type: named("Int") -// }), -// ( -// String::from("page_token"), -// ArgumentInfo { -// description: Some(String::from("A token corresponding to a specific page of results, as provided by metadata")), -// argument_type: nullable(named("String")) -// }), - - fn parse_list_templates_params( in_args: BTreeMap, ) -> Result { diff --git a/crates/ndc-sendgrid/src/schema.rs b/crates/ndc-sendgrid/src/schema.rs index b09b094..0673796 100644 --- a/crates/ndc-sendgrid/src/schema.rs +++ b/crates/ndc-sendgrid/src/schema.rs @@ -489,13 +489,72 @@ fn send_mail() -> ProcedureInfo { ProcedureInfo { name: String::from(SEND_MAIL), description: Some(String::from("Allows you to send email")), - arguments: BTreeMap::from([( - String::from("request"), - ArgumentInfo { - description: Some(String::from("Request parameters")), - argument_type: named("send_mail_request"), - }, - )]), + arguments: BTreeMap::from([ + (String::from("from"), + ArgumentInfo { + description: Some(String::from("An array of messages and their metadata. Each object within personalizations can be thought of as an envelope - it defines who should receive an individual message and how that message should be handled.")), + argument_type: named("String") }), + (String::from("to"), + ArgumentInfo { + description: Some(String::from("An address that will be sent the email.")), + argument_type: named("String") }), + (String::from("cc"), + ArgumentInfo { + description: Some(String::from("An address that will be cced the email.")), + argument_type: nullable(named("String")) }), + (String::from("bcc"), + ArgumentInfo { + description: Some(String::from("An address that will be bcced the email.")), + argument_type: nullable(named("String")) }), + (String::from("reply_to"), + ArgumentInfo { + description: Some(String::from("An array of recipients who will receive replies.")), + argument_type: nullable(named("String")) }), + (String::from("subject"), + ArgumentInfo { + description: Some(String::from("The subject of your email. See character length requirements according to RFC 2822.")), + argument_type: named("String") }), + // (String::from("content"), + // ArgumentInfo { + // description: Some(String::from("An array where you can specify the content of your email. You can include multiple MIME types of content, but you must specify at least one MIME type.")), + // argument_type: named("mail_content") }), + (String::from("content_type"), + ArgumentInfo { + description: Some(String::from("The MIME type of the content you are including in your email")), + argument_type: named("String") }), + (String::from("content"), + ArgumentInfo { + description: Some(String::from("The actual content of the specified MIME type that you are including in your email.")), + argument_type: named("String") }), + // (String::from("type"), ObjectField { + // r#type: named("String"), + // description: Some(String::from("The MIME type of the content you are including in your email")) + // }), + // (String::from("name"), ObjectField { + // r#type: named("String"), + // description: Some(String::from("The actual content of the specified MIME type that you are including in your email.")) + // }), + // (String::from("attachment"), + // ArgumentInfo { + // description: Some(String::from("An object where you can specify an attachment you want to include.")), + // argument_type: nullable(named("mail_attachment")) }), + (String::from("template_id"), + ArgumentInfo { + description: Some(String::from("An email template ID. A template that contains a subject and content — either text or html — will override any subject and content values specified at the personalizations or message level.")), + argument_type: nullable(named("String")) }), + (String::from("send_at"), + ArgumentInfo { + description: Some(String::from("A unix timestamp allowing you to specify when you want your email to be delivered. This may be overridden by the send_at parameter set at the personalizations level. Delivery cannot be scheduled more than 72 hours in advance.")), + argument_type: nullable(named("Int")) }), + (String::from("batch_id"), + ArgumentInfo { + description: Some(String::from("An ID representing a batch of emails to be sent at the same time. Including a batch_id in your request allows you include this email in that batch. It also enables you to cancel or pause the delivery of that batch.")), + argument_type: nullable(named("String")) }) //, + // (String::from("asm"), + // ArgumentInfo { + // description: Some(String::from("An object allowing you to specify how to handle unsubscribes.")), + // argument_type: nullable(named("unsubscription_settings")) }) + ]), result_type: named("send_mail_response"), } } diff --git a/crates/ndc-sendgrid/src/sendgrid_api.rs b/crates/ndc-sendgrid/src/sendgrid_api.rs index d9dedbc..a560215 100644 --- a/crates/ndc-sendgrid/src/sendgrid_api.rs +++ b/crates/ndc-sendgrid/src/sendgrid_api.rs @@ -99,12 +99,12 @@ impl Display for ErrorResponse { #[derive(Deserialize, Clone, Debug)] pub struct ErrorItem { pub message: String, - pub error_id: String, + pub field: String, } impl Display for ErrorItem { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}: {}", self.error_id, self.message) + write!(f, "{}: {}", self.field, self.message) } } @@ -120,6 +120,22 @@ pub enum RequestError { OtherError { error: String }, } +#[derive(Serialize, Clone, Debug)] +pub struct SimpleSendMailRequest { + pub from: MailAddress, + pub to: MailAddress, + pub cc: Option, + pub bcc: Option, + pub reply_to: Option, + pub subject: String, + pub content: MailContent, + pub attachment: Option, + pub template_id: Option, + pub send_at: Option, + pub batch_id: Option, + pub asm: Option, +} + #[derive(Serialize, Clone, Debug)] pub struct SendMailRequest { pub personalizations: Vec,