Skip to content

Commit

Permalink
Add functionality for servers, templates, webhooks (#32)
Browse files Browse the repository at this point in the history
* Server, Templates, Suppressions, Add account token to client
- create server
- get and delete suppressions
- copy/push templates to another server
  • Loading branch information
JennyShuZhan authored Sep 3, 2024
1 parent 3828e62 commit 24bed29
Show file tree
Hide file tree
Showing 17 changed files with 822 additions and 9 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
Cargo.lock

.DS_Store
.env
.idea/
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ thiserror = { version = "1.0" }
typed-builder = { version = "0.18" }
url = { version = "2.5" }
indexmap = { version = "2.2", features = ["serde"], optional = true }
time = { version = "0.3.17", features = ["serde-human-readable", "macros"] }

[features]
default = []
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use postmark::api::email::{SendEmailRequest,Body};

async fn send_email(){
let client = PostmarkClient::builder()
.token("<sometoken>")
.server_token("<sometoken>")
.build();

let req = SendEmailRequest::builder()
Expand Down
3 changes: 3 additions & 0 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ use serde::{Deserialize, Serialize};

pub mod bounce;
pub mod email;
pub mod message_streams;
pub mod server;
pub mod templates;
pub mod webhooks;

/// The body of a email message
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
Expand Down
30 changes: 30 additions & 0 deletions src/api/message_streams.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//! You'll find in templates sending related endpoints.
use serde::{Deserialize, Serialize};
use std::fmt;

mod delete_suppression;
mod get_suppressions;

pub use delete_suppression::*;
pub use get_suppressions::*;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub enum SuppressionStatusType {
#[default]
Deleted,
Failed,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum StreamIdOrName {
StreamId(String),
}

impl fmt::Display for StreamIdOrName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
StreamIdOrName::StreamId(id) => write!(f, "{}", id),
}
}
}
120 changes: 120 additions & 0 deletions src/api/message_streams/delete_suppression.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
use std::borrow::Cow;

use crate::api::message_streams::{StreamIdOrName, SuppressionStatusType};
use crate::Endpoint;
use serde::{Deserialize, Serialize};
use typed_builder::TypedBuilder;

#[derive(Debug, Clone, PartialEq, Serialize)]
#[serde(rename_all = "PascalCase")]
#[derive(TypedBuilder)]
pub struct DeleteSuppressionRequest {
#[serde(skip)]
pub stream_id: StreamIdOrName,
pub suppressions: Vec<Emails>,
}

#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct DeleteSuppressionResponse {
pub suppressions: Vec<DeleteSuppression>,
}

#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct DeleteSuppression {
pub email_address: String,
pub status: SuppressionStatusType,
pub message: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Emails {
pub email_address: String,
}

impl Endpoint for DeleteSuppressionRequest {
type Request = DeleteSuppressionRequest;
type Response = DeleteSuppressionResponse;

fn endpoint(&self) -> Cow<'static, str> {
format!("/message-streams/{}/suppressions/delete", self.stream_id).into()
}

fn body(&self) -> &Self::Request {
self
}
}

#[cfg(test)]
mod tests {
use crate::reqwest::PostmarkClient;
use crate::Query;
use httptest::matchers::request;
use httptest::{responders::*, Expectation, Server};
use serde_json::json;

use super::*;

const STREAM_ID: &str = "my-stream-id";

const EMAIL_1: &str = "good.address@wildbit.com";
const EMAIL_2: &str = "not.suppressed@wildbit.com";
const EMAIL_3: &str = "spammy.address@wildbit.com";
#[tokio::test]
pub async fn create_new_server() {
let server = Server::run();

server.expect(
Expectation::matching(request::method_path(
"POST",
format!("/message-streams/{STREAM_ID}/suppressions/delete"),
))
.respond_with(json_encoded(json!({
"Suppressions":[
{
"EmailAddress":EMAIL_1,
"Status":"Deleted",
"Message": null
},
{
"EmailAddress":EMAIL_2,
"Status":"Deleted",
"Message": null
},
{
"EmailAddress":EMAIL_3,
"Status":"Failed",
"Message": "You do not have the required authority to change this suppression."
}
]
}))),
);

let client = PostmarkClient::builder()
.base_url(server.url("/").to_string())
.build();

let req = DeleteSuppressionRequest::builder()
.stream_id(StreamIdOrName::StreamId(String::from(STREAM_ID)))
.suppressions(Vec::from([
Emails {
email_address: String::from(EMAIL_1),
},
Emails {
email_address: String::from(EMAIL_2),
},
Emails {
email_address: String::from(EMAIL_3),
},
]))
.build();

print!("{}\n", req.endpoint());

req.execute(&client)
.await
.expect("Should get a response and be able to json decode it");
}
}
109 changes: 109 additions & 0 deletions src/api/message_streams/get_suppressions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use std::borrow::Cow;

use crate::api::message_streams::StreamIdOrName;
use crate::Endpoint;
use serde::{Deserialize, Serialize};
use typed_builder::TypedBuilder;

use time::OffsetDateTime;

#[derive(Debug, Clone, PartialEq, Serialize)]
#[serde(rename_all = "PascalCase")]
#[derive(TypedBuilder)]
pub struct GetSuppressionRequest {
#[serde(skip)]
pub stream_id: StreamIdOrName,
}

#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct GetSuppressionResponse {
pub suppressions: Vec<GetSuppression>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct GetSuppression {
pub email_address: String,
pub suppression_reason: String,
pub origin: String,
#[serde(with = "time::serde::rfc3339")]
pub created_at: OffsetDateTime,
}

impl Endpoint for GetSuppressionRequest {
type Request = GetSuppressionRequest;
type Response = GetSuppressionResponse;

fn endpoint(&self) -> Cow<'static, str> {
format!("/message-streams/{}/suppressions/dump", self.stream_id).into()
}

fn body(&self) -> &Self::Request {
self
}

fn method(&self) -> http::Method {
http::Method::GET
}
}

#[cfg(test)]
mod tests {
use httptest::matchers::request;
use httptest::{responders::*, Expectation, Server};
use serde_json::json;

use crate::reqwest::PostmarkClient;
use crate::Query;

use super::*;
const STREAM_ID: &str = "my-stream-id";
#[tokio::test]
pub async fn create_new_server() {
let server = Server::run();

server.expect(
Expectation::matching(request::method_path(
"GET",
format!("/message-streams/{STREAM_ID}/suppressions/dump"),
))
.respond_with(json_encoded(json!({
"Suppressions":[
{
"EmailAddress":"address@wildbit.com",
"SuppressionReason":"ManualSuppression",
"Origin": "Recipient",
"CreatedAt":"2019-12-17T08:58:33-05:00"
},
{
"EmailAddress":"bounce.address@wildbit.com",
"SuppressionReason":"HardBounce",
"Origin": "Recipient",
"CreatedAt":"2019-12-17T08:58:33-05:00"
},
{
"EmailAddress":"spam.complaint.address@wildbit.com",
"SuppressionReason":"SpamComplaint",
"Origin": "Recipient",
"CreatedAt":"2019-12-17T08:58:33-05:00"
}
]
}))),
);

let client = PostmarkClient::builder()
.base_url(server.url("/").to_string())
.build();

let req = GetSuppressionRequest::builder()
.stream_id(StreamIdOrName::StreamId(String::from(STREAM_ID)))
.build();

print!("{}\n", req.endpoint());

req.execute(&client)
.await
.expect("Should get a response and be able to json decode it");
}
}
44 changes: 44 additions & 0 deletions src/api/server.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//! You'll find in email sending related endpoints.
pub use create_server::*;
pub use get_server::*;
use serde::{Deserialize, Serialize};
use std::fmt;

mod create_server;
mod get_server;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ServerIdOrName {
ServerId(isize),
ServerName(String),
}

#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub enum DeliveryType {
#[default]
Live,
Sandbox,
}

#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub enum ServerColor {
#[default]
Purple,
Blue,
Turquoise,
Green,
Red,
Yellow,
Grey,
Orange,
}

impl fmt::Display for ServerIdOrName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ServerIdOrName::ServerId(id) => write!(f, "{}", id),
ServerIdOrName::ServerName(name) => write!(f, "{}", name),
}
}
}
Loading

0 comments on commit 24bed29

Please sign in to comment.