Skip to content

Notify work-in-progress

A small wrapper around the AWS SNS Go SDK. Because even with all their docs, it's still a pain to get working.

See the repo.

go
// Package notify provides helpers for setting up, maintaining, and
// sending Push notifications with the AWS SNS SDK
package notify

import (
	"encoding/json"
	"errors"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/sns"
)

type Client struct {
	sns    *sns.SNS
	appArn *string
}

type Alert struct {
	Title        *string        `json:"title"`
	Body         *string        `json:"body"`
	LocKey       *string        `json:"loc-key,omitempty"`
	LocArgs      *[]interface{} `json:"loc-args,omitempty"`
	ActionLocKey *string        `json:"action-loc-key,omitempty"`
}

type Push struct {
	Alert *Alert      `json:"alert,omitempty"`
	Sound *string     `json:"sound,omitempty"`
	Data  interface{} `json:"custom_data,omitempty"`
	Badge *int        `json:"badge,omitempty"`
}

type iosPush struct {
	APS Push `json:"aps"`
}

type wrapper struct {
	APNS        string `json:"APNS"`
	APNSSandbox string `json:"APNS_SANDBOX"`
	Default     string `json:"default"`
}

// NewClient instantiates a new SNS client with the provided
// AWS session options.
func NewClient(applicationArn string, sessionOptions *session.Options) *Client {
	client := new(Client)
	client.appArn = &applicationArn

	sess := session.Must(session.NewSessionWithOptions(*sessionOptions))
	client.sns = sns.New(sess)
	return client
}

// Send a push notification to a specific Endpoint ARN.
func (client *Client) Send(arn string, data *Push) (*sns.PublishOutput, error) {
	msg := wrapper{}
	ios := iosPush{
		APS: *data,
	}
	b, err := json.Marshal(ios)
	if err != nil {
		return &sns.PublishOutput{}, err
	}
	msg.APNS = string(b[:])
	msg.APNSSandbox = msg.APNS

	pushData, err := json.Marshal(msg)
	if err != nil {
		return &sns.PublishOutput{}, err
	}
	m := string(pushData[:])
	params := &sns.PublishInput{
		Message:          aws.String(m),
		MessageStructure: aws.String("json"),
		TargetArn:        aws.String(arn),
	}
	result, err := client.sns.Publish(params)

	return result, err
}

// GetTokenArn retrieves the Endpoint ARN for the provided device token
func (client *Client) GetTokenArn(deviceToken string) (string, error) {

	var input = sns.ListEndpointsByPlatformApplicationInput{
		PlatformApplicationArn: client.appArn,
	}

	result, err := client.sns.ListEndpointsByPlatformApplication(&input)

	if err != nil {
		return "", err
	}

	// this will take forever if there are a lot of
	// app installs/subscribers
	for _, t := range result.Endpoints {
		// token attribute
		var tAtt = t.Attributes["Token"]
		// enabled attribute
		var eAtt = t.Attributes["Enabled"]

		// return the endpoint ARN if the device token
		// matches and the endpoint is enabled
		if *tAtt == deviceToken && *eAtt == "true" {
			return *t.EndpointArn, nil
		}
	}

	return "", errors.New("no valid match")
}

// Unregister SNS Endpoint / user device
func (client *Client) Unregister(endpointArn string) error {
	params := &sns.DeleteEndpointInput{
		EndpointArn: aws.String(endpointArn),
	}
	_, err := client.sns.DeleteEndpoint(params)
	return err
}

// Register the user device token into SNS
func (client *Client) Register(deviceToken string) (string, error) {

	params := &sns.CreatePlatformEndpointInput{
		PlatformApplicationArn: client.appArn,
		Token:                  aws.String(deviceToken),
		Attributes: map[string]*string{
			"Token":   aws.String(deviceToken),
			"Enabled": aws.String("true"),
		},
	}

	result, err := client.sns.CreatePlatformEndpoint(params)

	return *result.EndpointArn, err
}