/
client.go
152 lines (129 loc) · 3.92 KB
/
client.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
package github
import (
"context"
"encoding/json"
"errors"
"fmt"
"strings"
"sync"
"time"
"github.com/google/go-github/v47/github"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
"golang.org/x/oauth2"
"golang.org/x/sync/errgroup"
"github.com/opengapps/package-api/pkg/gapps"
"github.com/opengapps/package-api/internal/pkg/config"
"github.com/opengapps/package-api/internal/pkg/db"
"github.com/opengapps/package-api/internal/pkg/models"
)
type client struct {
cfg *viper.Viper
client *github.Client
storage Storage
once sync.Once
}
// NewClient creates new Github client
func NewClient(ctx context.Context, opts ...Option) (*client, error) {
c := &client{}
for _, opt := range opts {
if err := opt(c); err != nil {
return nil, fmt.Errorf("unable to create client: %w", err)
}
}
if c.cfg == nil {
return nil, errors.New("config is nil")
}
if c.storage == nil {
return nil, errors.New("storage is nil")
}
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: c.cfg.GetString(config.GithubTokenKey)},
)
c.client = github.NewClient(oauth2.NewClient(ctx, ts))
if c.client == nil {
return nil, errors.New("client for Github is nil")
}
return c, nil
}
func (c *client) Watch(ctx context.Context) {
c.once.Do(func() { c.watch(ctx) })
}
// watch starts the release watcher
func (c *client) watch(ctx context.Context) {
if err := c.checkRelease(ctx); err != nil {
log.WithError(err).Error("Unable to check for the latest release")
}
period := c.cfg.GetDuration(config.GithubWatchIntervalKey)
ticker := time.NewTicker(period)
for {
select {
case <-ctx.Done():
log.Warn("Context canceled, exiting watcher")
ticker.Stop()
return
case <-ticker.C:
if err := c.checkRelease(ctx); err != nil {
log.WithError(err).Error("Unable to check for the latest release")
}
}
}
}
func (c *client) checkRelease(ctx context.Context) error {
var resp models.ListResponse
g, gCtx := errgroup.WithContext(ctx)
for _, arch := range gapps.PlatformValues() {
g.Go(c.addPackageFn(gCtx, &resp, arch))
}
if err := g.Wait(); err != nil {
return err
}
// check results and save to DB if necessary
for arch, record := range resp.ArchList {
key := fmt.Sprintf(db.KeyTemplate, record.Date, arch)
_, err := c.storage.Get(key)
switch {
case err == nil:
// data is already there, continue
continue
case errors.Is(err, db.ErrNilValue), errors.Is(err, db.ErrNotFound):
// save the new data
var data []byte
dbRecord := db.Record{ArchRecord: record, Timestamp: time.Now().Unix()}
if data, err = json.Marshal(dbRecord); err != nil {
log.WithError(err).Errorf("Unable to marshal the data for the arch '%s' and date '%s'", arch, dbRecord.Date)
continue
}
if err = c.storage.Put(key, data); err != nil {
log.WithError(err).Errorf("Unable to save the data for the arch '%s' and date '%s'", arch, dbRecord.Date)
}
default:
return fmt.Errorf("unable to check DB key: %w", err)
}
}
return nil
}
func (c *client) addPackageFn(ctx context.Context, resp *models.ListResponse, arch gapps.Platform) func() error {
return func() error {
release, err := c.GetLatestRelease(ctx, arch)
if err != nil {
return err
}
for _, asset := range release.Assets {
for _, variant := range asset.Variants {
pkgAPI, err := gapps.AndroidString(strings.Replace(asset.API, ".", "", -1))
if err != nil {
return fmt.Errorf("unable to parse API '%s' in LATEST file for arch '%s': %w", asset.API, arch, err)
}
pkgVariant, err := gapps.VariantString(variant)
if err != nil {
return fmt.Errorf("unable to parse variant '%s' of API '%s' in LATEST file for arch '%s': %w", variant, asset.API, arch, err)
}
if err = resp.AddPackage(release.Date, arch, pkgAPI, pkgVariant); err != nil {
return fmt.Errorf("unable to add package for variant '%s' of API '%s' in LATEST file for arch '%s': %w", variant, asset.API, arch, err)
}
}
}
return nil
}
}