Documentation
¶
Overview ¶
Package postmarkmail provides a Postmark mail.Driver.
New requires a server token and accepts Builder callbacks for the send-email endpoint and borrowed HTTP client. Callbacks run directly once in order before validation; nil entries panic. Publication detaches the draft's settings, not the live HTTP client or transport. No request is made until Send, and there are no lifecycle hooks or native SDK accessors.
Send never follows redirects, including same-origin redirects, and returns a submission error with an empty receipt for responses with status 300 or higher. A per-send shallow client copy enforces that policy without modifying or closing a supplied client. Its transport and cookie jar remain shared. Trust custom endpoints and transports with both the server token and message contents. Inline attachments keep bare ContentID values in the portable message; Send adds Postmark's required cid: prefix on the wire. Driver value formatting suppresses credentials, but does not sanitize provider errors or message data.
Example:
driver, err := postmarkmail.New("server-token", func(transport *postmarkmail.Builder) {
transport.HTTPClient(&http.Client{Timeout: 10 * time.Second})
})
if err != nil {
panic(err)
}
_ = driver // Pass to mail.New to submit portable messages.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder edits a Postmark construction draft. The zero value selects the production endpoint and a client with a 30-second timeout. New copies settings after all callbacks; supplied HTTP clients remain borrowed live dependencies.
Example:
driver, err := postmarkmail.New("server-token", func(transport *postmarkmail.Builder) {
transport.HTTPClient(&http.Client{Timeout: 10 * time.Second})
})
if err != nil {
panic(err)
}
_ = driver
func (*Builder) Endpoint ¶
Endpoint replaces the send-email URL and returns the same draft. Empty uses https://api.postmarkapp.com/email. Like the original constructor, New does not validate a nonempty URL; malformed URLs fail when Send constructs its request.
Example:
driver, err := postmarkmail.New("server-token", func(transport *postmarkmail.Builder) {
transport.Endpoint("https://api.postmarkapp.com/email")
})
if err != nil {
panic(err)
}
_ = driver
func (*Builder) HTTPClient ¶
HTTPClient replaces the borrowed request client and returns the same draft. Nil restores a fresh client with a 30-second timeout. Send reads the client's current settings into a shallow copy and replaces only CheckRedirect: no redirects are followed and the supplied callback is not invoked. The caller's client is not modified or closed. Its transport and cookie jar remain shared, not cloned or closed; their ownership remains with the caller. Configure the client before concurrent use and trust its transport with credentials and mail.
Example:
client := &http.Client{Timeout: 10 * time.Second}
driver, err := postmarkmail.New("server-token", func(transport *postmarkmail.Builder) {
transport.HTTPClient(client)
})
if err != nil {
panic(err)
}
mailer := mail.New(driver)
_ = mailer // The client remains yours to manage.
Example ¶
ExampleBuilder_HTTPClient constructs a mailer with a borrowed HTTP client. Construction makes no request and does not transfer ownership of the client or its transport to the driver.
client := &http.Client{Timeout: 10 * time.Second}
driver, err := New("server-token", func(transport *Builder) {
transport.HTTPClient(client)
})
if err != nil {
fmt.Println(err)
return
}
mailer := mail.New(driver)
fmt.Println(mailer.Driver() == driver)
Output: true
type BuilderFunc ¶
type BuilderFunc = func(*Builder)
BuilderFunc edits one Postmark construction draft. New runs callbacks once in order before validation and copies completed settings; nil entries panic. The alias accepts ordinary functions and function slices without conversion.
Example:
var timeout postmarkmail.BuilderFunc = func(transport *postmarkmail.Builder) {
transport.HTTPClient(&http.Client{Timeout: 10 * time.Second})
}
driver, err := postmarkmail.New("server-token", timeout)
if err != nil {
panic(err)
}
_ = mail.New(driver)
type Driver ¶
type Driver struct {
// contains filtered or unexported fields
}
Driver submits mail.Message values through the Postmark API. Each Send applies the no-redirect policy without modifying a borrowed client. Format hides credentials and connection settings; a zero Driver is not usable.
Example:
driver, err := postmarkmail.New("server-token")
if err != nil {
panic(err)
}
_ = mail.New(driver)
func New ¶
func New(token string, configure ...BuilderFunc) (*Driver, error)
New creates a Postmark Driver authenticating with token.
token is the Postmark server token, sent as X-Postmark-Server-Token. Callbacks apply Builder.Endpoint and Builder.HTTPClient. No request is made until Driver.Send.
Callbacks run directly once, synchronously in order on one fresh draft before validation. Nil entries panic; expanded nil or empty slices omit edits. Publication copies settings, not the supplied live HTTP client or its transport.
Send never follows redirects, including same-origin redirects, so a redirect cannot forward the server token or replay the message. Responses with status 300 or higher return a submission error and an empty receipt. Each send uses a shallow client copy with its own redirect policy, ignoring any supplied CheckRedirect callback without modifying or closing the caller's client. The transport and cookie jar remain shared; configure them before use and trust custom transports with the token and message. A custom endpoint must be the trusted final URL, not a redirecting URL.
New returns an error when token is empty.
Example:
driver, err := postmarkmail.New("server-token")
if err != nil {
panic(err)
}
mailer := mail.New(driver)
_ = mailer // Use Send with a request context and an immutable message.
Example ¶
ExampleNew constructs the default driver and mail facade without network I/O. A synthetic token is sufficient until Send; production callers supply their own server token and handle construction errors before using the mailer.
package main
import (
"fmt"
"arkoren.dev/core/service/mail"
"arkoren.dev/driver/postmark/postmarkmail"
)
func main() {
driver, err := postmarkmail.New("server-token")
if err != nil {
fmt.Println(err)
return
}
mailer := mail.New(driver)
fmt.Println(mailer.Driver() == driver)
}
Output: true
func (Driver) Format ¶
Format replaces fmt's value output with a fixed type label, ignoring the supplied verb and flags, so reflection cannot disclose the server token. Its value receiver protects pointers and copied values without formatting endpoints or client internals. The formatter has no error return, so fmt-owned writer errors cannot propagate. The fmt package still handles type (%T) and pointer (%p) verbs itself.
Example:
driver, err := postmarkmail.New("server-token")
if err != nil {
panic(err)
}
text := fmt.Sprintf("%+v", driver) // "postmarkmail.Driver"
_ = text
Example ¶
ExampleDriver_Format demonstrates the public formatting protocol's credential boundary without constructing a request or revealing the synthetic token.
package main
import (
"fmt"
"arkoren.dev/driver/postmark/postmarkmail"
)
func main() {
driver, err := postmarkmail.New("server-token")
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%#v\n", driver)
}
Output: postmarkmail.Driver
func (*Driver) Send ¶
Send validates an immutable mail.Message and submits its snapshot through Postmark. The message must include a sender. Validation, encoding, HTTP, and provider failures return an empty receipt without changing the message.
Example using a configured driver and request context:
message := mail.NewMessage(mail.Recipients{
To: []mail.Address{{Email: "person@example.com"}},
}, mail.Body{Text: "Hello."}, func(letter *mail.MessageBuilder) {
letter.From(mail.Address{Email: "sender@example.com"})
})
receipt, err := driver.Send(ctx, message)
if err != nil {
return err
}
_ = receipt.ID