-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathloco.go
168 lines (133 loc) · 3.39 KB
/
loco.go
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package main
import (
"bytes"
"crypto/sha1"
"encoding/hex"
"errors"
"io"
"log"
"net/http"
"sync"
)
// Hashes represents translations hashes indexed by domain
type Hashes map[string]string
// TranslationsProvider is an interface for providing translation Hashes
type TranslationsProvider interface {
// Fetch translation hashes and return a pointer to the Hashes struct.
// The Hashes pointer must remain the same after future calls.
Fetch() *Hashes
}
// LocoProvider is the Loco implementation of the translation TranslationsProvider
type LocoProvider struct {
clients map[string]*LocoClient
hashes *Hashes
}
// NewLocoProvider creates a new LocoProvider instance
func NewLocoProvider(clients map[string]*LocoClient) *LocoProvider {
return &LocoProvider{clients, &Hashes{}}
}
// Fetch loco translations by domain
func (p *LocoProvider) Fetch() *Hashes {
var wg sync.WaitGroup
errChan := make(chan error)
defer close(errChan)
// Compute hashes for all translation domains in parallel
for k, v := range p.clients {
wg.Add(1)
go func(domain string, client *LocoClient) {
defer wg.Done()
body, err := client.exportAll()
if err != nil {
errChan <- err
}
defer body.Close()
buf := new(bytes.Buffer)
buf.ReadFrom(body)
hash := sha1.New()
hash.Write(buf.Bytes())
(*p.hashes)[domain] = hex.EncodeToString(hash.Sum(nil))
}(k, v)
}
wgDone := make(chan bool)
go func() {
wg.Wait()
close(wgDone)
}()
select {
case <-wgDone:
break
case err := <-errChan:
close(errChan)
log.Fatal(err)
}
log.Printf("Refreshed hashes: %+v\n", p.hashes)
return p.hashes
}
// LocoClient represents a Loco client
type LocoClient struct {
http *http.Client
project string
apiKey string
baseURI string
apiVersion string
}
// NewClient creates a new Client
func NewClient(project string, apiKey string) *LocoClient {
http := &http.Client{}
return &LocoClient{
http,
project,
apiKey,
"https://localise.biz",
"1.0.25",
}
}
// CreateLocoClients creates loco clients for every entry of the domain => api key map
func CreateLocoClients(apiKeys map[string]string) map[string]*LocoClient {
// Create Loco clients
var clients = make(map[string]*LocoClient)
for k, v := range apiKeys {
clients[k] = NewClient(k, v)
}
// Check API connectivity (parallel)
log.Println("Checking connectivity to Loco API...")
for domain, client := range clients {
err := client.authVerify()
if err != nil {
log.Fatal(errors.New("Authentication to Loco API for domain " + domain + " failed: " + err.Error()))
}
}
return clients
}
// AuthVerify will check the authentication credentials
func (c LocoClient) authVerify() error {
resp, err := c.call("GET", "/api/auth/verify")
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode == 401 {
return errors.New("Authentication failed")
}
return nil
}
// ExportAll returns the export of all traslations
func (c LocoClient) exportAll() (io.ReadCloser, error) {
resp, err := c.call("GET", "/api/export/all")
if err != nil {
return nil, err
}
return resp.Body, nil
}
func (c LocoClient) call(method string, path string) (*http.Response, error) {
req, err := http.NewRequest(method, c.baseURI+path+"?key="+c.apiKey, nil)
if err != nil {
return nil, err
}
req.Header.Add("X-Api-Version", c.apiVersion)
resp, err := c.http.Do(req)
if err != nil {
return nil, err
}
return resp, nil
}