/
mlp_service.go
116 lines (100 loc) · 3.06 KB
/
mlp_service.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
package services
import (
"context"
"fmt"
"log"
"net/http"
"time"
mlp "github.com/caraml-dev/mlp/api/client"
"github.com/caraml-dev/mlp/api/pkg/auth"
"github.com/caraml-dev/xp/management-service/errors"
"github.com/patrickmn/go-cache"
)
const (
mlpCacheExpirySeconds = 600
mlpCacheCleanUpSeconds = 900
mlpQueryTimeoutSeconds = 30
)
// MLPService provides a set of methods to interact with the MLP APIs
type MLPService interface {
// GetProject gets the project matching the provided id
GetProject(id int64) (*mlp.Project, error)
}
type mlpService struct {
mlpClient *mlpClient
cache *cache.Cache
}
type mlpClient struct {
api *mlp.APIClient
}
func newMLPClient(googleClient *http.Client, basePath string) *mlpClient {
cfg := mlp.NewConfiguration()
cfg.BasePath = basePath
cfg.HTTPClient = googleClient
return &mlpClient{mlp.NewAPIClient(cfg)}
}
// NewMLPService returns a service that retrieves information that is shared across MLP projects.
func NewMLPService(mlpBasePath string) (MLPService, error) {
httpClient := http.DefaultClient
googleClient, err := auth.InitGoogleClient(context.Background())
if err == nil {
httpClient = googleClient
} else {
log.Println("Google default credential not found. Fallback to HTTP default client")
}
svc := &mlpService{
mlpClient: newMLPClient(httpClient, mlpBasePath),
cache: cache.New(mlpCacheExpirySeconds*time.Second, mlpCacheCleanUpSeconds*time.Second),
}
err = svc.refreshProjects()
if err != nil {
return nil, err
}
return svc, nil
}
// GetProject gets the project matching the provided id. This method will hit the cache first,
// and if not found, will call MLP API once to get the updated list of projects and refresh the cache,
// then try to get the value again. If still not found, will return a freecache NotFound error.
func (service mlpService) GetProject(id int64) (*mlp.Project, error) {
project, err := service.getProject(id)
if err != nil {
err = service.refreshProjects()
if err != nil {
return nil, err
}
return service.getProject(id)
}
return project, nil
}
func (service mlpService) getProject(id int64) (*mlp.Project, error) {
key := buildProjectKey(id)
cachedValue, found := service.cache.Get(key)
if !found {
return nil, errors.Newf(errors.NotFound, "MLP Project info for id %d not found in the cache", id)
}
// Cast the data
project, ok := cachedValue.(mlp.Project)
if !ok {
return nil, errors.Newf(errors.NotFound, "Malformed project info found in the cache for id %d", id)
}
return &project, nil
}
func (service mlpService) refreshProjects() error {
ctx, cancel := context.WithTimeout(context.Background(), mlpQueryTimeoutSeconds*time.Second)
defer cancel()
projects, resp, err := service.mlpClient.api.ProjectApi.V1ProjectsGet(ctx, nil)
if err != nil {
return err
}
if resp != nil && resp.Body != nil {
defer resp.Body.Close()
}
for _, project := range projects {
key := buildProjectKey(int64(project.ID))
service.cache.Set(key, project, cache.DefaultExpiration)
}
return nil
}
func buildProjectKey(id int64) string {
return fmt.Sprintf("proj:%d", id)
}