external
External third-party service adapters following the FC/IS pattern. Five adapters: SMTP, IMAP, Twilio, an FTPS file source, and a SOAP 1.1 client.
|
Note
|
Stripe payments have moved to payments library. |
Key namespaces
wagoe.external.schema — Malli schemas for all five adapters
wagoe.external.ports — Protocols: ISmtpProvider, IImapMailbox,
ITwilioMessaging, IFileSource, ISoapClient
wagoe.external.core.smtp — Pure SMTP helpers
wagoe.external.core.imap — Pure IMAP helpers
wagoe.external.core.mail-tls — Pure TLS settings shared by SMTP and IMAP
wagoe.external.core.twilio — Pure Twilio helpers (build SMS/WhatsApp params)
wagoe.external.core.ftps — Pure FTPS helpers
wagoe.external.core.soap — Pure SOAP envelope building and parsing
wagoe.external.shell.adapters.smtp — SmtpProviderAdapter (Jakarta Mail)
wagoe.external.shell.adapters.imap — ImapMailboxAdapter (Jakarta Mail, UIDFolder)
wagoe.external.shell.adapters.twilio — TwilioAdapter (clj-http, Basic auth)
wagoe.external.shell.adapters.ftps — FtpsFileSource (Commons Net, explicit TLS)
wagoe.external.shell.adapters.soap — SoapClient (java.net.http, SOAP 1.1)
Enabling adapters
All five adapters ship as :inactive in config.edn. Move to :active to enable:
;; In :active section of resources/conf/dev/config.edn
:wagoe.external/twilio
{:account-sid #env TWILIO_ACCOUNT_SID
:auth-token #env TWILIO_AUTH_TOKEN
:from-number #env TWILIO_FROM_NUMBER}
:wagoe.external/smtp
{:host #env SMTP_HOST
:port 587
:username #env SMTP_USERNAME
:password #env SMTP_PASSWORD
:tls? true}
:wagoe.external/imap
{:host #env IMAP_HOST
:port 993
:username #env IMAP_USERNAME
:password #env IMAP_PASSWORD
:ssl? true}
:wagoe.external/ftps
{:host #env FTPS_HOST
:username #env FTPS_USERNAME
:password #env FTPS_PASSWORD}
:wagoe.external/soap
{:endpoint #env SOAP_ENDPOINT
:contract "IList"
:username #env SOAP_USERNAME
:password #env SOAP_PASSWORD}
TLS trust and certificate names
The SMTP and IMAP adapters check that the server’s certificate is trusted by the
JVM and names :host. Two settings relax that, for a relay reached by IP or a
provider alias:
:ssl-trust "relay.internal" ; "*" or hosts, string or vector
:check-server-identity? false ; default true
:ssl-trust accepts any certificate from the listed hosts, self-signed or
expired; "*" does so for every host. :check-server-identity? false accepts a
certificate issued for another name. Either one lets someone who can intercept
the connection read the credentials and the mail, so prefer adding the server’s
CA to the JVM trust store and connecting by the name on the certificate.
Trusting a host does not skip the name check; set both when the certificate is
self-signed for another name.
Twilio SMS / WhatsApp
(require '[wagoe.external.ports :as ports])
;; Send SMS
(ports/send-sms! twilio {:to "+31612345678" :body "Your code: 123456"})
;; Send WhatsApp
(ports/send-whatsapp! twilio {:to "+31612345678" :body "Your order has shipped!"})
FTPS file imports
IFileSource lists a directory and downloads files. The connection uses explicit
TLS with an encrypted data channel, passive mode and binary transfers, and the
server certificate is checked against the JVM’s trust store, or :trust-store.
(ports/list-files! ftps "/outbox" {:match #"contacts-.*\.csv" :skip already-imported})
;; => {:success? true :files [{:name "contacts-1.csv" :path "/outbox/contacts-1.csv" :size 812 ...}]}
(ports/fetch-file! ftps "/outbox/contacts-1.csv")
;; => {:success? true :content <bytes>}
Transient failures (DNS, refused connection, timeouts, 4xx replies) are retried
with backoff, :max-attempts times in all. A 5xx reply such as a rejected login
fails at once. A failure returns {:success? false :error {:type :external-error …}},
with :host, :attempts and :cause.
SOAP services
ISoapClient calls an operation on a SOAP 1.1 service such as WCF. Operations are
described in config, not read from a WSDL: the SOAPAction defaults to
namespace + :contract + / + operation, and :operations overrides it, the
timeout and more per operation.
(ports/call! client :GetListEntries
[[:Sitename "town"]
[:filterRequest {:Sitename "town" :ListId list-id :SinceDate "2026-10-01T00:00:00"}]])
;; => {:success? true :result {:list-entries [...] :status "0"} :body {...}}
Element names are sent as written. A map is written in DataContract’s default
order, ordinal by name; pass [name value] pairs where the contract declares
another order. Nested members use :data-namespace. The response has kebab-case
keys and string values: xsi:nil is nil, WCF dictionaries are maps and arrays
vectors. Name an array that may hold one item in the operation’s :arrays, or it
reads as a map.
A SOAP fault returns {:success? false :error {:type :fault :code … :message …}};
no usable answer returns :type :external-error. Faults listed in :retry-faults
(a throttling message, say), refused connections and 429s are retried with
backoff; timeouts and 5xx are retried only for operations that are
:idempotent?, the default. Credentials go as HTTP Basic or, with
:credential-elements, as leading parameters, and are never logged.
Testing
clojure -M:test:test/mail:test/ftp :external