├── .gitignore ├── project.clj ├── README.markdown ├── LICENSE.markdown ├── CHANGES.md ├── test └── postmark │ └── test │ └── core.clj ├── docs └── index.md └── src └── postmark └── core.clj /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *jar 3 | .cake 4 | /.lein-* 5 | /checkouts 6 | /classes 7 | /docs/build 8 | /lib 9 | /target 10 | pom.xml 11 | pom.xml.asc 12 | scratch.clj 13 | .nrepl-port 14 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject postmark "1.4.2-SNAPSHOT" 2 | :description "Clojure bindings for http://postmarkapp.com/" 3 | :url "https://github.com/sjl/clojure-postmark" 4 | :scm {:name "git" 5 | :url "https://github.com/sjl/clojure-postmark"} 6 | :license {:name "MIT" 7 | :url "https://github.com/sjl/clojure-postmark/blob/master/LICENSE.markdown"} 8 | :exclusions [org.clojure/clojure] 9 | :deploy-repositories [["releases" :clojars] 10 | ["snapshots" :clojars]] 11 | :dependencies [[org.clojure/clojure "1.8.0"] 12 | [cheshire "5.8.0"] 13 | [clj-http "3.7.0"]] 14 | :plugins [[lein-ancient "0.6.10"] 15 | [lein-kibit "0.1.5"] 16 | [jonase/eastwood "0.2.4"]] 17 | :aliases {"lint" ["do" ["ancient"] ["kibit"] ["eastwood"]]}) 18 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | clojure-postmark 2 | ================ 3 | 4 | [![Dependencies Status](https://versions.deps.co/danielcompton/clojure-postmark/status.svg)](https://versions.deps.co/danielcompton/clojure-postmark) 5 | [![Clojars Project](https://img.shields.io/clojars/v/postmark.svg)](https://clojars.org/postmark) 6 | 7 | `clojure-postmark` lets you talk to [Postmark](https://postmarkapp.com/) from Clojure. 8 | 9 | This project was originally created by [Steve Losh](http://stevelosh.com) and is maintained by [Daniel Compton](https://danielcompton.net). 10 | 11 | * Documentation: 12 | * Source (Git): 13 | * Issues: 14 | * License: [MIT/X11](http://www.opensource.org/licenses/mit-license.php) 15 | -------------------------------------------------------------------------------- /LICENSE.markdown: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2015 Steve Losh and contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | ## [Unreleased] 9 | 10 | ## [1.4.0] - 2017-01-31 11 | 12 | ### Added 13 | 14 | - This changelog 15 | 16 | ### Changed 17 | 18 | - New release, now under `[postmark 1.4.0]` as I (Daniel Compton) have taken over maintaining clojure-postmark. 19 | - Docs tweaked 20 | 21 | ## [1.3.2] - 2016-06-27 22 | 23 | ### Added 24 | 25 | - Allow library consumers to override the from email address per email 26 | 27 | ## [1.3.1] - 2016-06-27 28 | 29 | ### Added 30 | 31 | - Support for sending custom mail headers with the `:headers` key 32 | 33 | ## [1.3.0] - 2016-03-11 34 | 35 | ### Changed 36 | 37 | - Released under `[org.clojars.danielcompton/postmark "1.3.0"]` 38 | - Support sending to up to fifty recipients at once 39 | - Clean up code style 40 | - Use the HTTPS Postmark API 41 | 42 | 43 | [Unreleased]: https://github.com/danielcompton/clojure-postmark/compare/1.3.2...HEAD 44 | [1.4.0]: https://github.com/danielcompton/clojure-postmark/compare/1.3.2...1.4.0 45 | [1.3.2]: https://github.com/danielcompton/clojure-postmark/compare/1.3.1...1.3.2 46 | [1.3.1]: https://github.com/danielcompton/clojure-postmark/compare/1.3.0...1.3.1 47 | [1.3.0]: https://github.com/danielcompton/clojure-postmark/compare/v1.1.0...1.3.0 48 | -------------------------------------------------------------------------------- /test/postmark/test/core.clj: -------------------------------------------------------------------------------- 1 | (ns postmark.test.core 2 | (:require [postmark.core] 3 | [cheshire.core :refer [generate-string parse-string]] 4 | [clojure.test :refer :all])) 5 | 6 | (deftest test-mail-to-json 7 | (is (= (@#'postmark.core/mail-to-json {"Subject" "foo" "Nothing" nil}) 8 | (generate-string {"Subject" "foo"})) 9 | "parse mail")) 10 | 11 | (deftest test-get-to-string 12 | (is (= (@#'postmark.core/to-string nil) 13 | nil) 14 | "get-to-string didn't handle nil") 15 | 16 | (is (= (@#'postmark.core/to-string "foo") 17 | "foo") 18 | "get-to-string didn't pass a string through") 19 | 20 | (is (= (@#'postmark.core/to-string ["foo"]) 21 | "foo") 22 | "get-to-string didn't pass a string in a seq through") 23 | 24 | (is (= (@#'postmark.core/to-string ["foo" "bar"]) 25 | "foo,bar") 26 | "get-to-string didn't join a seq properly")) 27 | 28 | (deftest test-postmark-test 29 | (testing "Do Postmark test function and API key work?" 30 | (let [test-rsp ((postmark.core/postmark-test "from-address@example.com") {:to ["fluffy@example.com" "sprinkles@example.com"] 31 | :subject "Testing" 32 | :text "I might want your noms."})] 33 | (is (= 200 (:status test-rsp))) 34 | (is (= "fluffy@example.com,sprinkles@example.com" (-> test-rsp :body :To))) 35 | (is (= "Test job accepted" (-> test-rsp :body :Message))) 36 | (is (zero? (-> test-rsp :body :ErrorCode)))))) 37 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # clojure-postmark 2 | 3 | `clojure-postmark` lets you talk to [Postmark](http://postmarkapp.com/) from 4 | Clojure. 5 | 6 | * Documentation: 7 | * Source (Git): 8 | * Issues: 9 | * License: [MIT/X11](http://www.opensource.org/licenses/mit-license.php) 10 | 11 | 12 | Installation 13 | ============ 14 | 15 | To get started just slap this in your `project.clj` `:dependencies`: 16 | 17 | [postmark "1.4.0"] 18 | 19 | `clojure-postmark` works with Clojure 1.8.0. 20 | 21 | 22 | Usage 23 | ===== 24 | 25 | First you need to load the `postmark` function: 26 | 27 | ```clj 28 | ; In an (ns) 29 | (:use [postmark.core :only (postmark)]) 30 | 31 | ; Outside an (ns) 32 | (use '[postmark.core :only (postmark)]) 33 | ``` 34 | 35 | Create a customized `postmark` function: 36 | 37 | ```clj 38 | (def pm (postmark "YOUR_API_KEY" "from-address@example.com")) 39 | ``` 40 | 41 | Now just call the function to send an email: 42 | 43 | ```clj 44 | (pm {:to "fluffy@example.com" 45 | :subject "Your Noms" 46 | :text "I wants them."}) 47 | ``` 48 | 49 | You can send to multiple addresses by using a seq for `:to`, but remember that 50 | Postmark's API won't let you send to more than twenty recipients at a time: 51 | 52 | ```clj 53 | (pm {:to ["fluffy@example.com" "sprinkles@example.com"] 54 | :subject "All of Your Noms" 55 | :text "I wants them."}) 56 | ``` 57 | 58 | There are a few other keys you can use in the map you pass to the call: 59 | 60 | ```clj 61 | (pm {:to ["fluffy@example.com" "sprinkles@example.com"] 62 | :cc ["haiku@example.com"] 63 | :bcc ["admin@example.com"] 64 | :subject "All of Your Noms" 65 | :text "I wants them." 66 | :html "I wants them." 67 | :tag "Noms" 68 | :reply-to "avedon@example.com"}) 69 | ``` 70 | 71 | ## Testing 72 | 73 | If you just want to run a test you can use `postmark-test` without an API key 74 | instead of `postmark`: 75 | 76 | ```clj 77 | (use '[postmark.core :only (postmark-test)]) 78 | (def pt (postmark-test "from-address@example.com")) 79 | 80 | (pt {:to ["fluffy@example.com" "sprinkles@example.com"] 81 | :subject "Testing" 82 | :text "I might want your noms."}) 83 | ``` 84 | 85 | --- 86 | 87 | Created by [Steve Losh](http://stevelosh.com), and maintained by [Daniel Compton](https://danielcompton.net). 88 | 89 | 90 | -------------------------------------------------------------------------------- /src/postmark/core.clj: -------------------------------------------------------------------------------- 1 | (ns postmark.core 2 | (:require [clj-http.client :as client] 3 | [clojure.string :as string] 4 | [cheshire.core :as json])) 5 | 6 | (defn- mail-to-json 7 | "Return the JSON for the given mail map. 8 | 9 | Falsey values will be removed from the map before serializing." 10 | [mail] 11 | (->> mail 12 | (filter (comp some? val)) ;; Filter out keys with nil values 13 | (into {}) 14 | (json/generate-string))) 15 | 16 | (defn- send-to-postmark [path api-key mail] 17 | (client/post (str "https://api.postmarkapp.com/" path) 18 | {:body (mail-to-json mail) 19 | :headers {"X-Postmark-Server-Token" api-key} 20 | :content-type :json 21 | :accept :json 22 | :coerce :always 23 | :as :json})) 24 | 25 | (defn- to-string 26 | "Create a string appropriate for the to/cc/bcc fields in a Postmark call. 27 | 28 | Can be passed a email address as a string like 'foo@bar.com' or 29 | 'Foo Bar ', or a seq of such strings." 30 | [to] 31 | (when to 32 | (if (string? to) 33 | to 34 | (string/join "," to)))) 35 | 36 | (defn- no-more-than-50-recipients [to] 37 | (or (string? to) 38 | (<= (count to) 50))) 39 | 40 | (defn- mail 41 | "Send an email with the Postmark API. 42 | 43 | Remember: Postmark only lets you send to at most fifty addresses at once." 44 | [api-key default-from {:keys [to subject cc bcc from tag text html reply-to headers]}] 45 | {:pre [(no-more-than-50-recipients to)]} 46 | (send-to-postmark "email/" api-key 47 | {"From" (or from default-from) 48 | "To" (to-string to) 49 | "Subject" subject 50 | "Cc" (to-string cc) 51 | "Bcc" (to-string bcc) 52 | "Tag" tag 53 | "TextBody" text 54 | "HtmlBody" html 55 | "ReplyTo" reply-to 56 | "Headers" headers})) 57 | 58 | 59 | (defn postmark [api-key from] 60 | (partial mail api-key from)) 61 | 62 | (def postmark-test-api-key "POSTMARK_API_TEST") 63 | 64 | (defn postmark-test [from] 65 | (postmark postmark-test-api-key from)) 66 | 67 | (defn mail-with-template 68 | [api-key from {:keys [to cc bcc tag reply-to template-id template-model]}] 69 | (send-to-postmark "email/withTemplate/" api-key 70 | {"From" from 71 | "To" (to-string to) 72 | "Cc" (to-string cc) 73 | "Bcc" (to-string bcc) 74 | "Tag" tag 75 | "ReplyTo" reply-to 76 | "TemplateId" template-id 77 | "TemplateModel" template-model})) 78 | 79 | --------------------------------------------------------------------------------