feat: switch glide to govendor. (#186)

* feat: switch glide to govendor.

* fix: testing
This commit is contained in:
Bo-Yi Wu
2017-02-19 15:04:00 +08:00
committed by GitHub
parent 3f4485edf7
commit 23bd0d0c56
732 changed files with 252501 additions and 52 deletions

22
vendor/github.com/sideshow/apns2/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2016 Adam Jones
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

158
vendor/github.com/sideshow/apns2/README.md generated vendored Normal file
View File

@@ -0,0 +1,158 @@
# APNS/2
APNS/2 is a go package designed for simple, flexible and fast Apple Push Notifications on iOS, OSX and Safari using the new HTTP/2 Push provider API.
[![Build Status](https://travis-ci.org/sideshow/apns2.svg?branch=master)](https://travis-ci.org/sideshow/apns2) [![Coverage Status](https://coveralls.io/repos/sideshow/apns2/badge.svg?branch=master&service=github)](https://coveralls.io/github/sideshow/apns2?branch=master) [![GoDoc](https://godoc.org/github.com/sideshow/apns2?status.svg)](https://godoc.org/github.com/sideshow/apns2)
## Features
- Uses new Apple APNs HTTP/2 connection
- Works with go 1.6 and later
- Supports new iOS 10 features such as Collapse IDs, Subtitles and Mutable Notifications
- Supports persistent connections to APNs
- Supports VoIP/PushKit notifications (iOS 8 and later)
- Fast, modular & easy to use
- Tested and working in APNs production environment
## Install
- Make sure you have [Go](https://golang.org/doc/install) installed and have set your [GOPATH](https://golang.org/doc/code.html#GOPATH).
- Download and install the dependencies:
```sh
go get -u golang.org/x/net/http2
go get -u golang.org/x/crypto/pkcs12
```
- Install apns2:
```sh
go get -u github.com/sideshow/apns2
```
## Example
```go
package main
import (
"log"
"fmt"
"github.com/sideshow/apns2"
"github.com/sideshow/apns2/certificate"
)
func main() {
cert, err := certificate.FromP12File("../cert.p12", "")
if err != nil {
log.Fatal("Cert Error:", err)
}
notification := &apns2.Notification{}
notification.DeviceToken = "11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"
notification.Topic = "com.sideshow.Apns2"
notification.Payload = []byte(`{"aps":{"alert":"Hello!"}}`) // See Payload section below
client := apns2.NewClient(cert).Production()
res, err := client.Push(notification)
if err != nil {
log.Fatal("Error:", err)
}
fmt.Printf("%v %v %v\n", res.StatusCode, res.ApnsID, res.Reason)
}
```
## Notification
At a minimum, a _Notification_ needs a _DeviceToken_, a _Topic_ and a _Payload_.
```go
notification := &Notification{
DeviceToken: "11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7",
Topic: "com.sideshow.Apns2",
Payload: []byte(`{"aps":{"alert":"Hello!"}}`),
}
```
You can also set an optional _ApnsID_, _Expiration_ or _Priority_.
```go
notification.ApnsID = "40636A2C-C093-493E-936A-2A4333C06DEA"
notification.Expiration = time.Now()
notification.Priority = apns.PriorityLow
```
## Payload
You can use raw bytes for the `notification.Payload` as above, or you can use the payload builder package which makes it easy to construct APNs payloads.
```go
// {"aps":{"alert":"hello","badge":1},"key":"val"}
payload := NewPayload().Alert("hello").Badge(1).Custom("key", "val")
notification.Payload = payload
client.Push(notification)
```
Refer to the [payload](https://godoc.org/github.com/sideshow/apns2/payload) docs for more info.
## Response, Error handling
APNS/2 draws the distinction between a valid response from Apple indicating whether or not the _Notification_ was sent or not, and an unrecoverable or unexpected _Error_;
- An `Error` is returned if a non-recoverable error occurs, i.e. if there is a problem with the underlying _http.Client_ connection or _Certificate_, the payload was not sent, or a valid _Response_ was not received.
- A `Response` is returned if the payload was successfully sent to Apple and a documented response was received. This struct will contain more information about whether or not the push notification succeeded, its _apns-id_ and if applicable, more information around why it did not succeed.
To check if a `Notification` was successfully sent;
```go
res, err := client.Push(notification)
if err != nil {
log.Println("There was an error", err)
return
}
if res.Sent() {
log.Println("Sent:", res.ApnsID)
} else {
fmt.Printf("Not Sent: %v %v %v\n", res.StatusCode, res.ApnsID, res.Reason)
}
```
## Command line tool
APNS/2 has a command line tool that can be installed with `go get github.com/sideshow/apns2/apns2`. Usage:
```
apns2 --help
usage: apns2 --certificate-path=CERTIFICATE-PATH --topic=TOPIC [<flags>]
Listens to STDIN to send notifications and writes APNS response code and reason to STDOUT.
The expected format is: <DeviceToken> <APNS Payload>
Example: aff0c63d9eaa63ad161bafee732d5bc2c31f66d552054718ff19ce314371e5d0 {"aps": {"alert": "hi"}}
Flags:
--help Show context-sensitive help (also try --help-long and --help-man).
-c, --certificate-path=CERTIFICATE-PATH
Path to certificate file.
-t, --topic=TOPIC The topic of the remote notification, which is typically the bundle ID for your app
-m, --mode="production" APNS server to send notifications to. `production` or `development`. Defaults to `production`
--version Show application version.
```
## License
The MIT License (MIT)
Copyright (c) 2016 Adam Jones
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,128 @@
// Package certificate contains functions to load an Apple APNs PKCS#12
// or PEM certificate from either an in memory byte array or a local file.
package certificate
import (
"crypto"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"io/ioutil"
"strings"
"golang.org/x/crypto/pkcs12"
)
// Possible errors when parsing a certificate.
var (
ErrFailedToDecryptKey = errors.New("failed to decrypt private key")
ErrFailedToParsePKCS1PrivateKey = errors.New("failed to parse PKCS1 private key")
ErrFailedToParseCertificate = errors.New("failed to parse certificate PEM data")
ErrNoPrivateKey = errors.New("no private key")
ErrNoCertificate = errors.New("no certificate")
)
// FromP12File loads a PKCS#12 certificate from a local file and returns a
// tls.Certificate.
//
// Use "" as the password argument if the pem certificate is not password
// protected.
func FromP12File(filename string, password string) (tls.Certificate, error) {
p12bytes, err := ioutil.ReadFile(filename)
if err != nil {
return tls.Certificate{}, err
}
return FromP12Bytes(p12bytes, password)
}
// FromP12Bytes loads a PKCS#12 certificate from an in memory byte array and
// returns a tls.Certificate.
//
// Use "" as the password argument if the PKCS#12 certificate is not password
// protected.
func FromP12Bytes(bytes []byte, password string) (tls.Certificate, error) {
key, cert, err := pkcs12.Decode(bytes, password)
if err != nil {
return tls.Certificate{}, err
}
return tls.Certificate{
Certificate: [][]byte{cert.Raw},
PrivateKey: key,
Leaf: cert,
}, nil
}
// FromPemFile loads a PEM certificate from a local file and returns a
// tls.Certificate. This function is similar to the crypto/tls LoadX509KeyPair
// function, however it supports PEM files with the cert and key combined
// in the same file, as well as password protected key files which are both
// common with APNs certificates.
//
// Use "" as the password argument if the PEM certificate is not password
// protected.
func FromPemFile(filename string, password string) (tls.Certificate, error) {
bytes, err := ioutil.ReadFile(filename)
if err != nil {
return tls.Certificate{}, err
}
return FromPemBytes(bytes, password)
}
// FromPemBytes loads a PEM certificate from an in memory byte array and
// returns a tls.Certificate. This function is similar to the crypto/tls
// X509KeyPair function, however it supports PEM files with the cert and
// key combined, as well as password protected keys which are both common with
// APNs certificates.
//
// Use "" as the password argument if the PEM certificate is not password
// protected.
func FromPemBytes(bytes []byte, password string) (tls.Certificate, error) {
var cert tls.Certificate
var block *pem.Block
for {
block, bytes = pem.Decode(bytes)
if block == nil {
break
}
if block.Type == "CERTIFICATE" {
cert.Certificate = append(cert.Certificate, block.Bytes)
}
if block.Type == "PRIVATE KEY" || strings.HasSuffix(block.Type, "PRIVATE KEY") {
key, err := unencryptPrivateKey(block, password)
if err != nil {
return tls.Certificate{}, err
}
cert.PrivateKey = key
}
}
if len(cert.Certificate) == 0 {
return tls.Certificate{}, ErrNoCertificate
}
if cert.PrivateKey == nil {
return tls.Certificate{}, ErrNoPrivateKey
}
if c, e := x509.ParseCertificate(cert.Certificate[0]); e == nil {
cert.Leaf = c
}
return cert, nil
}
func unencryptPrivateKey(block *pem.Block, password string) (crypto.PrivateKey, error) {
if x509.IsEncryptedPEMBlock(block) {
bytes, err := x509.DecryptPEMBlock(block, []byte(password))
if err != nil {
return nil, ErrFailedToDecryptKey
}
return parsePrivateKey(bytes)
}
return parsePrivateKey(block.Bytes)
}
func parsePrivateKey(bytes []byte) (crypto.PrivateKey, error) {
key, err := x509.ParsePKCS1PrivateKey(bytes)
if err != nil {
return nil, ErrFailedToParsePKCS1PrivateKey
}
return key, nil
}

140
vendor/github.com/sideshow/apns2/client.go generated vendored Normal file
View File

@@ -0,0 +1,140 @@
// Package apns2 is a go Apple Push Notification Service (APNs) provider that
// allows you to send remote notifications to your iOS, tvOS, and OS X
// apps, using the new APNs HTTP/2 network protocol.
package apns2
import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"time"
"golang.org/x/net/http2"
)
// Apple HTTP/2 Development & Production urls
const (
HostDevelopment = "https://api.development.push.apple.com"
HostProduction = "https://api.push.apple.com"
)
// DefaultHost is a mutable var for testing purposes
var DefaultHost = HostDevelopment
var (
// TLSDialTimeout is the maximum amount of time a dial will wait for a connect
// to complete.
TLSDialTimeout = 20 * time.Second
// HTTPClientTimeout specifies a time limit for requests made by the
// HTTPClient. The timeout includes connection time, any redirects,
// and reading the response body.
HTTPClientTimeout = 30 * time.Second
)
// Client represents a connection with the APNs
type Client struct {
HTTPClient *http.Client
Certificate tls.Certificate
Host string
}
// NewClient returns a new Client with an underlying http.Client configured with
// the correct APNs HTTP/2 transport settings. It does not connect to the APNs
// until the first Notification is sent via the Push method.
//
// As per the Apple APNs Provider API, you should keep a handle on this client
// so that you can keep your connections with APNs open across multiple
// notifications; dont repeatedly open and close connections. APNs treats rapid
// connection and disconnection as a denial-of-service attack.
//
// If your use case involves multiple long-lived connections, consider using
// the ClientManager, which manages clients for you.
func NewClient(certificate tls.Certificate) *Client {
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{certificate},
}
if len(certificate.Certificate) > 0 {
tlsConfig.BuildNameToCertificate()
}
transport := &http2.Transport{
TLSClientConfig: tlsConfig,
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
return tls.DialWithDialer(&net.Dialer{Timeout: TLSDialTimeout}, network, addr, cfg)
},
}
return &Client{
HTTPClient: &http.Client{
Transport: transport,
Timeout: HTTPClientTimeout,
},
Certificate: certificate,
Host: DefaultHost,
}
}
// Development sets the Client to use the APNs development push endpoint.
func (c *Client) Development() *Client {
c.Host = HostDevelopment
return c
}
// Production sets the Client to use the APNs production push endpoint.
func (c *Client) Production() *Client {
c.Host = HostProduction
return c
}
// Push sends a Notification to the APNs gateway. If the underlying http.Client
// is not currently connected, this method will attempt to reconnect
// transparently before sending the notification. It will return a Response
// indicating whether the notification was accepted or rejected by the APNs
// gateway, or an error if something goes wrong.
func (c *Client) Push(n *Notification) (*Response, error) {
payload, err := json.Marshal(n)
if err != nil {
return nil, err
}
url := fmt.Sprintf("%v/3/device/%v", c.Host, n.DeviceToken)
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(payload))
setHeaders(req, n)
httpRes, err := c.HTTPClient.Do(req)
if err != nil {
return nil, err
}
defer httpRes.Body.Close()
response := &Response{}
response.StatusCode = httpRes.StatusCode
response.ApnsID = httpRes.Header.Get("apns-id")
decoder := json.NewDecoder(httpRes.Body)
if err := decoder.Decode(&response); err != nil && err != io.EOF {
return &Response{}, err
}
return response, nil
}
func setHeaders(r *http.Request, n *Notification) {
r.Header.Set("Content-Type", "application/json; charset=utf-8")
if n.Topic != "" {
r.Header.Set("apns-topic", n.Topic)
}
if n.ApnsID != "" {
r.Header.Set("apns-id", n.ApnsID)
}
if n.CollapseID != "" {
r.Header.Set("apns-collapse-id", n.CollapseID)
}
if n.Priority > 0 {
r.Header.Set("apns-priority", fmt.Sprintf("%v", n.Priority))
}
if !n.Expiration.IsZero() {
r.Header.Set("apns-expiration", fmt.Sprintf("%v", n.Expiration.Unix()))
}
}

162
vendor/github.com/sideshow/apns2/client_manager.go generated vendored Normal file
View File

@@ -0,0 +1,162 @@
package apns2
import (
"container/list"
"crypto/sha1"
"crypto/tls"
"sync"
"time"
)
type managerItem struct {
key [sha1.Size]byte
client *Client
lastUsed time.Time
}
// ClientManager is a way to manage multiple connections to the APNs.
type ClientManager struct {
// MaxSize is the maximum number of clients allowed in the manager. When
// this limit is reached, the least recently used client is evicted. Set
// zero for no limit.
MaxSize int
// MaxAge is the maximum age of clients in the manager. Upon retrieval, if
// a client has remained unused in the manager for this duration or longer,
// it is evicted and nil is returned. Set zero to disable this
// functionality.
MaxAge time.Duration
// Factory is the function which constructs clients if not found in the
// manager.
Factory func(certificate tls.Certificate) *Client
cache map[[sha1.Size]byte]*list.Element
ll *list.List
mu sync.Mutex
once sync.Once
}
// NewClientManager returns a new ClientManager for prolonged, concurrent usage
// of multiple APNs clients. ClientManager is flexible enough to work best for
// your use case. When a client is not found in the manager, Get will return
// the result of calling Factory, which can be a Client or nil.
//
// Having multiple clients per certificate in the manager is not allowed.
//
// By default, MaxSize is 64, MaxAge is 10 minutes, and Factory always returns
// a Client with default options.
func NewClientManager() *ClientManager {
manager := &ClientManager{
MaxSize: 64,
MaxAge: 10 * time.Minute,
Factory: NewClient,
}
manager.initInternals()
return manager
}
// Add adds a Client to the manager. You can use this to individually configure
// Clients in the manager.
func (m *ClientManager) Add(client *Client) {
m.initInternals()
m.mu.Lock()
defer m.mu.Unlock()
key := cacheKey(client.Certificate)
now := time.Now()
if ele, hit := m.cache[key]; hit {
item := ele.Value.(*managerItem)
item.client = client
item.lastUsed = now
m.ll.MoveToFront(ele)
return
}
ele := m.ll.PushFront(&managerItem{key, client, now})
m.cache[key] = ele
if m.MaxSize != 0 && m.ll.Len() > m.MaxSize {
m.mu.Unlock()
m.removeOldest()
m.mu.Lock()
}
}
// Get gets a Client from the manager. If a Client is not found in the manager
// or if a Client has remained in the manager longer than MaxAge, Get will call
// the ClientManager's Factory function, store the result in the manager if
// non-nil, and return it.
func (m *ClientManager) Get(certificate tls.Certificate) *Client {
m.initInternals()
m.mu.Lock()
defer m.mu.Unlock()
key := cacheKey(certificate)
now := time.Now()
if ele, hit := m.cache[key]; hit {
item := ele.Value.(*managerItem)
if m.MaxAge != 0 && item.lastUsed.Before(now.Add(-m.MaxAge)) {
c := m.Factory(certificate)
if c == nil {
return nil
}
item.client = c
}
item.lastUsed = now
m.ll.MoveToFront(ele)
return item.client
}
c := m.Factory(certificate)
if c == nil {
return nil
}
m.mu.Unlock()
m.Add(c)
m.mu.Lock()
return c
}
// Len returns the current size of the ClientManager.
func (m *ClientManager) Len() int {
if m.cache == nil {
return 0
}
m.mu.Lock()
defer m.mu.Unlock()
return m.ll.Len()
}
func (m *ClientManager) initInternals() {
m.once.Do(func() {
m.cache = map[[sha1.Size]byte]*list.Element{}
m.ll = list.New()
})
}
func (m *ClientManager) removeOldest() {
m.mu.Lock()
ele := m.ll.Back()
m.mu.Unlock()
if ele != nil {
m.removeElement(ele)
}
}
func (m *ClientManager) removeElement(e *list.Element) {
m.mu.Lock()
defer m.mu.Unlock()
m.ll.Remove(e)
delete(m.cache, e.Value.(*managerItem).key)
}
func cacheKey(certificate tls.Certificate) [sha1.Size]byte {
var data []byte
for _, cert := range certificate.Certificate {
data = append(data, cert...)
}
return sha1.Sum(data)
}

80
vendor/github.com/sideshow/apns2/notification.go generated vendored Normal file
View File

@@ -0,0 +1,80 @@
package apns2
import (
"encoding/json"
"time"
)
const (
// PriorityLow will tell APNs to send the push message at a time that takes
// into account power considerations for the device. Notifications with this
// priority might be grouped and delivered in bursts. They are throttled, and
// in some cases are not delivered.
PriorityLow = 5
// PriorityHigh will tell APNs to send the push message immediately.
// Notifications with this priority must trigger an alert, sound, or badge on
// the target device. It is an error to use this priority for a push
// notification that contains only the content-available key.
PriorityHigh = 10
)
// Notification represents the the data and metadata for a APNs Remote Notification.
type Notification struct {
// An optional canonical UUID that identifies the notification. The canonical
// form is 32 lowercase hexadecimal digits, displayed in five groups separated
// by hyphens in the form 8-4-4-4-12. An example UUID is as follows:
//
// 123e4567-e89b-12d3-a456-42665544000
//
// If you don't set this, a new UUID is created by APNs and returned in the
// response.
ApnsID string
// A string which allows multiple notifications with the same collapse identifier
// to be displayed to the user as a single notification. The value should not
// exceed 64 bytes.
CollapseID string
// A string containing hexadecimal bytes of the device token for the target device.
DeviceToken string
// The topic of the remote notification, which is typically the bundle ID for
// your app. The certificate you create in the Apple Developer Member Center
// must include the capability for this topic. If your certificate includes
// multiple topics, you must specify a value for this header. If you omit this
// header and your APNs certificate does not specify multiple topics, the APNs
// server uses the certificates Subject as the default topic.
Topic string
// An optional time at which the notification is no longer valid and can be
// discarded by APNs. If this value is in the past, APNs treats the
// notification as if it expires immediately and does not store the
// notification or attempt to redeliver it. If this value is left as the
// default (ie, Expiration.IsZero()) an expiration header will not added to the
// http request.
Expiration time.Time
// The priority of the notification. Specify ether apns.PriorityHigh (10) or
// apns.PriorityLow (5) If you don't set this, the APNs server will set the
// priority to 10.
Priority int
// A byte array containing the JSON-encoded payload of this push notification.
// Refer to "The Remote Notification Payload" section in the Apple Local and
// Remote Notification Programming Guide for more info.
Payload interface{}
}
// MarshalJSON converts the notification payload to JSON.
func (n *Notification) MarshalJSON() ([]byte, error) {
switch n.Payload.(type) {
case string:
return []byte(n.Payload.(string)), nil
case []byte:
return n.Payload.([]byte), nil
default:
return json.Marshal(n.Payload)
}
}

291
vendor/github.com/sideshow/apns2/payload/builder.go generated vendored Normal file
View File

@@ -0,0 +1,291 @@
// Package payload is a helper package which contains a payload
// builder to make constructing notification payloads easier.
package payload
import "encoding/json"
// Payload represents a notification which holds the content that will be
// marshalled as JSON.
type Payload struct {
content map[string]interface{}
}
type aps struct {
Alert interface{} `json:"alert,omitempty"`
Badge interface{} `json:"badge,omitempty"`
Category string `json:"category,omitempty"`
ContentAvailable int `json:"content-available,omitempty"`
MutableContent int `json:"mutable-content,omitempty"`
Sound string `json:"sound,omitempty"`
ThreadID string `json:"thread-id,omitempty"`
URLArgs []string `json:"url-args,omitempty"`
}
type alert struct {
Action string `json:"action,omitempty"`
ActionLocKey string `json:"action-loc-key,omitempty"`
Body string `json:"body,omitempty"`
LaunchImage string `json:"launch-image,omitempty"`
LocArgs []string `json:"loc-args,omitempty"`
LocKey string `json:"loc-key,omitempty"`
Title string `json:"title,omitempty"`
Subtitle string `json:"subtitle,omitempty"`
TitleLocArgs []string `json:"title-loc-args,omitempty"`
TitleLocKey string `json:"title-loc-key,omitempty"`
}
// NewPayload returns a new Payload struct
func NewPayload() *Payload {
return &Payload{
map[string]interface{}{
"aps": &aps{},
},
}
}
// Alert sets the aps alert on the payload.
// This will display a notification alert message to the user.
//
// {"aps":{"alert":alert}}`
func (p *Payload) Alert(alert interface{}) *Payload {
p.aps().Alert = alert
return p
}
// Badge sets the aps badge on the payload.
// This will display a numeric badge on the app icon.
//
// {"aps":{"badge":b}}
func (p *Payload) Badge(b int) *Payload {
p.aps().Badge = b
return p
}
// ZeroBadge sets the aps badge on the payload to 0.
// This will clear the badge on the app icon.
//
// {"aps":{"badge":0}}
func (p *Payload) ZeroBadge() *Payload {
p.aps().Badge = 0
return p
}
// UnsetBadge removes the badge attribute from the payload.
// This will leave the badge on the app icon unchanged.
// If you wish to clear the app icon badge, use ZeroBadge() instead.
//
// {"aps":{}}
func (p *Payload) UnsetBadge() *Payload {
p.aps().Badge = nil
return p
}
// Sound sets the aps sound on the payload.
// This will play a sound from the app bundle, or the default sound otherwise.
//
// {"aps":{"sound":sound}}
func (p *Payload) Sound(sound string) *Payload {
p.aps().Sound = sound
return p
}
// ContentAvailable sets the aps content-available on the payload to 1.
// This will indicate to the app that there is new content available to download
// and launch the app in the background.
//
// {"aps":{"content-available":1}}
func (p *Payload) ContentAvailable() *Payload {
p.aps().ContentAvailable = 1
return p
}
// MutableContent sets the aps mutable-content on the payload to 1.
// This will indicate to the to the system to call your Notification Service
// extension to mutate or replace the notification's content.
//
// {"aps":{"mutable-content":1}}
func (p *Payload) MutableContent() *Payload {
p.aps().MutableContent = 1
return p
}
// Custom payload
// Custom sets a custom key and value on the payload.
// This will add custom key/value data to the notification payload at root level.
//
// {"aps":{}, key:value}
func (p *Payload) Custom(key string, val interface{}) *Payload {
p.content[key] = val
return p
}
// Alert dictionary
// AlertTitle sets the aps alert title on the payload.
// This will display a short string describing the purpose of the notification.
// Apple Watch & Safari display this string as part of the notification interface.
//
// {"aps":{"alert":{"title":title}}}
func (p *Payload) AlertTitle(title string) *Payload {
p.aps().alert().Title = title
return p
}
// AlertTitleLocKey sets the aps alert title localization key on the payload.
// This is the key to a title string in the Localizable.strings file for the
// current localization. See Localized Formatted Strings in Apple documentation
// for more information.
//
// {"aps":{"alert":{"title-loc-key":key}}}
func (p *Payload) AlertTitleLocKey(key string) *Payload {
p.aps().alert().TitleLocKey = key
return p
}
// AlertTitleLocArgs sets the aps alert title localization args on the payload.
// These are the variable string values to appear in place of the format
// specifiers in title-loc-key. See Localized Formatted Strings in Apple
// documentation for more information.
//
// {"aps":{"alert":{"title-loc-args":args}}}
func (p *Payload) AlertTitleLocArgs(args []string) *Payload {
p.aps().alert().TitleLocArgs = args
return p
}
// AlertSubtitle sets the aps alert subtitle on the payload.
// This will display a short string describing the purpose of the notification.
// Apple Watch & Safari display this string as part of the notification interface.
//
// {"aps":{"subtitle":"subtitle"}}
func (p *Payload) AlertSubtitle(subtitle string) *Payload {
p.aps().alert().Subtitle = subtitle
return p
}
// AlertBody sets the aps alert body on the payload.
// This is the text of the alert message.
//
// {"aps":{"alert":{"body":body}}}
func (p *Payload) AlertBody(body string) *Payload {
p.aps().alert().Body = body
return p
}
// AlertLaunchImage sets the aps launch image on the payload.
// This is the filename of an image file in the app bundle. The image is used
// as the launch image when users tap the action button or move the action
// slider.
//
// {"aps":{"alert":{"launch-image":image}}}
func (p *Payload) AlertLaunchImage(image string) *Payload {
p.aps().alert().LaunchImage = image
return p
}
// AlertLocArgs sets the aps alert localization args on the payload.
// These are the variable string values to appear in place of the format
// specifiers in loc-key. See Localized Formatted Strings in Apple
// documentation for more information.
//
// {"aps":{"alert":{"loc-args":args}}}
func (p *Payload) AlertLocArgs(args []string) *Payload {
p.aps().alert().LocArgs = args
return p
}
// AlertLocKey sets the aps alert localization key on the payload.
// This is the key to an alert-message string in the Localizable.strings file
// for the current localization. See Localized Formatted Strings in Apple
// documentation for more information.
//
// {"aps":{"alert":{"loc-key":key}}}
func (p *Payload) AlertLocKey(key string) *Payload {
p.aps().alert().LocKey = key
return p
}
// AlertAction sets the aps alert action on the payload.
// This is the label of the action button, if the user sets the notifications
// to appear as alerts. This label should be succinct, such as “Details” or
// “Read more”. If omitted, the default value is “Show”.
//
// {"aps":{"alert":{"action":action}}}
func (p *Payload) AlertAction(action string) *Payload {
p.aps().alert().Action = action
return p
}
// AlertActionLocKey sets the aps alert action localization key on the payload.
// This is the the string used as a key to get a localized string in the current
// localization to use for the notfication right buttons title instead of
// “View”. See Localized Formatted Strings in Apple documentation for more
// information.
//
// {"aps":{"alert":{"action-loc-key":key}}}
func (p *Payload) AlertActionLocKey(key string) *Payload {
p.aps().alert().ActionLocKey = key
return p
}
// General
// Category sets the aps category on the payload.
// This is a string value that represents the identifier property of the
// UIMutableUserNotificationCategory object you created to define custom actions.
//
// {"aps":{"alert":{"category":category}}}
func (p *Payload) Category(category string) *Payload {
p.aps().Category = category
return p
}
// Mdm sets the mdm on the payload.
// This is for Apple Mobile Device Management (mdm) payloads.
//
// {"aps":{}:"mdm":mdm}
func (p *Payload) Mdm(mdm string) *Payload {
p.content["mdm"] = mdm
return p
}
// ThreadID sets the aps thread id on the payload.
// This is for the purpose of updating the contents of a View Controller in a
// Notification Content app extension when a new notification arrives. If a
// new notification arrives whose thread-id value matches the thread-id of the
// notification already being displayed, the didReceiveNotification method
// is called.
//
// {"aps":{"thread-id":id}}
func (p *Payload) ThreadID(threadID string) *Payload {
p.aps().ThreadID = threadID
return p
}
// URLArgs sets the aps category on the payload.
// This specifies an array of values that are paired with the placeholders
// inside the urlFormatString value of your website.json file.
// See Apple Notification Programming Guide for Websites.
//
// {"aps":{"url-args":urlArgs}}
func (p *Payload) URLArgs(urlArgs []string) *Payload {
p.aps().URLArgs = urlArgs
return p
}
// MarshalJSON returns the JSON encoded version of the Payload
func (p *Payload) MarshalJSON() ([]byte, error) {
return json.Marshal(p.content)
}
func (p *Payload) aps() *aps {
return p.content["aps"].(*aps)
}
func (a *aps) alert() *alert {
if _, ok := a.Alert.(*alert); !ok {
a.Alert = &alert{}
}
return a.Alert.(*alert)
}

153
vendor/github.com/sideshow/apns2/response.go generated vendored Normal file
View File

@@ -0,0 +1,153 @@
package apns2
import (
"net/http"
"strconv"
"time"
)
// StatusSent is a 200 response.
const StatusSent = http.StatusOK
// The possible Reason error codes returned from APNs.
// From table 8-6 in the Apple Local and Remote Notification Programming Guide.
const (
// 400 The collapse identifier exceeds the maximum allowed size
ReasonBadCollapseID = "BadCollapseId"
// 400 The specified device token was bad. Verify that the request contains a
// valid token and that the token matches the environment.
ReasonBadDeviceToken = "BadDeviceToken"
// 400 The apns-expiration value is bad.
ReasonBadExpirationDate = "BadExpirationDate"
// 400 The apns-id value is bad.
ReasonBadMessageID = "BadMessageId"
// 400 The apns-priority value is bad.
ReasonBadPriority = "BadPriority"
// 400 The apns-topic was invalid.
ReasonBadTopic = "BadTopic"
// 400 The device token does not match the specified topic.
ReasonDeviceTokenNotForTopic = "DeviceTokenNotForTopic"
// 400 One or more headers were repeated.
ReasonDuplicateHeaders = "DuplicateHeaders"
// 400 Idle time out.
ReasonIdleTimeout = "IdleTimeout"
// 400 The device token is not specified in the request :path. Verify that the
// :path header contains the device token.
ReasonMissingDeviceToken = "MissingDeviceToken"
// 400 The apns-topic header of the request was not specified and was
// required. The apns-topic header is mandatory when the client is connected
// using a certificate that supports multiple topics.
ReasonMissingTopic = "MissingTopic"
// 400 The message payload was empty.
ReasonPayloadEmpty = "PayloadEmpty"
// 400 Pushing to this topic is not allowed.
ReasonTopicDisallowed = "TopicDisallowed"
// 403 The certificate was bad.
ReasonBadCertificate = "BadCertificate"
// 403 The client certificate was for the wrong environment.
ReasonBadCertificateEnvironment = "BadCertificateEnvironment"
// 403 The provider token is stale and a new token should be generated.
ReasonExpiredProviderToken = "ExpiredProviderToken"
// 403 The specified action is not allowed.
ReasonForbidden = "Forbidden"
// 403 The provider token is not valid or the token signature could not be
// verified.
ReasonInvalidProviderToken = "InvalidProviderToken"
// 403 No provider certificate was used to connect to APNs and Authorization
// header was missing or no provider token was specified.
ReasonMissingProviderToken = "MissingProviderToken"
// 404 The request contained a bad :path value.
ReasonBadPath = "BadPath"
// 405 The specified :method was not POST.
ReasonMethodNotAllowed = "MethodNotAllowed"
// 410 The device token is inactive for the specified topic.
ReasonUnregistered = "Unregistered"
// 413 The message payload was too large. See Creating the Remote Notification
// Payload in the Apple Local and Remote Notification Programming Guide for
// details on maximum payload size.
ReasonPayloadTooLarge = "PayloadTooLarge"
// 429 The provider token is being updated too often.
ReasonTooManyProviderTokenUpdates = "TooManyProviderTokenUpdates"
// 429 Too many requests were made consecutively to the same device token.
ReasonTooManyRequests = "TooManyRequests"
// 500 An internal server error occurred.
ReasonInternalServerError = "InternalServerError"
// 503 The service is unavailable.
ReasonServiceUnavailable = "ServiceUnavailable"
// 503 The server is shutting down.
ReasonShutdown = "Shutdown"
)
// Response represents a result from the APNs gateway indicating whether a
// notification was accepted or rejected and (if applicable) the metadata
// surrounding the rejection.
type Response struct {
// The HTTP status code retuened by APNs.
// A 200 value indicates that the notification was successfully sent.
// For a list of other possible status codes, see table 6-4 in the Apple Local
// and Remote Notification Programming Guide.
StatusCode int
// The APNs error string indicating the reason for the notification failure (if
// any). The error code is specified as a string. For a list of possible
// values, see the Reason constants above.
// If the notification was accepted, this value will be "".
Reason string
// The APNs ApnsID value from the Notification. If you didn't set an ApnsID on the
// Notification, this will be a new unique UUID which has been created by APNs.
ApnsID string
// If the value of StatusCode is 410, this is the last time at which APNs
// confirmed that the device token was no longer valid for the topic.
Timestamp Time
}
// Sent returns whether or not the notification was successfully sent.
// This is the same as checking if the StatusCode == 200.
func (c *Response) Sent() bool {
return c.StatusCode == StatusSent
}
// Time represents a device uninstall time
type Time struct {
time.Time
}
// UnmarshalJSON converts an epoch date into a Time struct.
func (t *Time) UnmarshalJSON(b []byte) error {
ts, err := strconv.ParseInt(string(b), 10, 64)
if err != nil {
return err
}
t.Time = time.Unix(ts/1000, 0)
return nil
}