-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclient.go
More file actions
126 lines (106 loc) · 2.88 KB
/
client.go
File metadata and controls
126 lines (106 loc) · 2.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package fcm
import (
"bytes"
"cmp"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
)
const defaultEndpoint = "https://fcm.googleapis.com/v1"
// Client for the Firebase Cloud Messaging (FCM) service.
type Client struct {
httpClient httpClient
endpoint string
project string
version string
}
type Config struct {
Client httpClient
Credentials []byte
ProjectID string
Endpoint string
}
type httpClient interface {
Do(req *http.Request) (*http.Response, error)
}
// NewClient creates a new instance of the Firebase Cloud Messaging Client.
func NewClient(cfg Config) (*Client, error) {
switch {
case len(cfg.Credentials) == 0:
return nil, errors.New("credentials not provided")
case cfg.ProjectID == "":
return nil, errors.New("project ID is required to access Firebase Cloud Messaging client")
}
if cfg.Client == nil {
trans, err := newHTTPClient(cfg.Credentials)
if err != nil {
return nil, fmt.Errorf("cannot create HTTP client: %w", err)
}
cfg.Client = trans
}
sendEndpoint := cmp.Or(cfg.Endpoint, defaultEndpoint)
return &Client{
httpClient: cfg.Client,
endpoint: fmt.Sprintf("%s/projects/%s/messages:send", sendEndpoint, cfg.ProjectID),
version: "github.com/cristalhq/fcm",
}, nil
}
// Send a [Message] to Firebase Cloud Messaging (FCM).
//
// The Message must specify exactly one of Token, Topic and Condition fields.
// FCM will customize the message for each target platform based on the arguments specified in the [Message].
func (c *Client) Send(ctx context.Context, message *Message) (*Response, error) {
if err := validateMessage(message); err != nil {
return nil, err
}
return c.send(ctx, message)
}
func (c *Client) send(ctx context.Context, message *Message) (*Response, error) {
msg := struct {
Message *Message `json:"message"`
}{
Message: message,
}
body, err := json.Marshal(msg)
if err != nil {
return nil, err
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.endpoint, bytes.NewBuffer(body))
if err != nil {
return nil, err
}
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("c.httpClient.Do: %w", err)
}
b, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("io.ReadAll: %w", err)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("code: %d, body: '%s", resp.StatusCode, string(b))
}
var response Response
if err := json.Unmarshal(b, &response); err != nil {
var errResp fcmErrorResponse
if err := json.Unmarshal(b, &errResp); err != nil {
return nil, fmt.Errorf("json.Unmarshal(b, &errResp): %w", err)
}
return nil, fmt.Errorf("json.Unmarshal(b, &resp): %w", err)
}
return &response, nil
}
type Response struct {
Name string `json:"name"`
}
type fcmErrorResponse struct {
Error struct {
Details []struct {
Type string `json:"@type"`
ErrorCode string `json:"errorCode"`
}
} `json:"error"`
}