/
read_api.go
133 lines (113 loc) · 3.88 KB
/
read_api.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
package concept
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
tidUtils "github.com/Financial-Times/transactionid-utils-go"
log "github.com/sirupsen/logrus"
)
type ReadAPI interface {
GetConceptsByIDs(ctx context.Context, ids []string) (map[string]Concept, error)
Endpoint() string
GTG() error
}
type internalConcordancesAPI struct {
endpoint string
username string
password string
httpClient *http.Client
batchSize int
}
func NewReadAPI(client *http.Client, endpoint string, username string, password string, batchSize int) ReadAPI {
return &internalConcordancesAPI{
endpoint: endpoint,
username: username,
password: password,
httpClient: client,
batchSize: batchSize,
}
}
var ErrUnexpectedResponse = errors.New("concept search API returned a non-200 HTTP status code")
func (search *internalConcordancesAPI) GetConceptsByIDs(ctx context.Context, conceptIDs []string) (map[string]Concept, error) {
tid, err := tidUtils.GetTransactionIDFromContext(ctx)
if err != nil {
tid = tidUtils.NewTransactionID()
log.WithField(tidUtils.TransactionIDKey, tid).
WithError(err).
Info("No Transaction ID provided for concept request, so a new one has been generated.")
ctx = tidUtils.TransactionAwareContext(ctx, tid)
}
var conceptIDsBatch []string
combinedResult := make(map[string]Concept)
n := len(conceptIDs)
for i := 0; i < n; i++ {
conceptIDsBatch = append(conceptIDsBatch, conceptIDs[i])
if ((i+1)%search.batchSize == 0) && (i != 0) || (i+1 == n) {
conceptsBatch, err := search.searchConceptBatch(ctx, conceptIDsBatch)
if err != nil {
log.WithError(err).WithField(tidUtils.TransactionIDKey, tid).Info("Failed to fetch concepts batch")
return nil, err
}
for uuid, c := range conceptsBatch {
combinedResult[uuid] = c
}
conceptIDsBatch = []string{}
}
}
log.WithField(tidUtils.TransactionIDKey, tid).Info("Concepts information fetched successfully")
return combinedResult, nil
}
func (search *internalConcordancesAPI) searchConceptBatch(ctx context.Context, conceptIDs []string) (map[string]Concept, error) {
tid, _ := tidUtils.GetTransactionIDFromContext(ctx)
batchConceptsLog := log.WithField(tidUtils.TransactionIDKey, tid)
req, err := http.NewRequest("GET", search.endpoint, nil)
if err != nil {
batchConceptsLog.WithError(err).Error("Error in creating the HTTP request to concept search API")
return nil, err
}
req.SetBasicAuth(search.username, search.password)
q := req.URL.Query()
for _, id := range conceptIDs {
q.Add("ids", id)
}
req.URL.RawQuery = q.Encode()
resp, err := search.httpClient.Do(req.WithContext(ctx))
if err != nil {
batchConceptsLog.WithError(err).Error("Error making the HTTP request to concept search API")
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, e := io.ReadAll(resp.Body)
if e != nil {
err = fmt.Errorf("status %d: %w", resp.StatusCode, ErrUnexpectedResponse)
} else {
err = fmt.Errorf("status %d %s: %w", resp.StatusCode, string(body), ErrUnexpectedResponse)
}
batchConceptsLog.WithError(err).Error("Error received from concept search API")
return nil, err
}
var result SearchResult
err = json.NewDecoder(resp.Body).Decode(&result)
if err != nil {
batchConceptsLog.WithError(err).Error("Error in unmarshalling the HTTP response from concept search API")
return nil, err
}
return result.Concepts, nil
}
func (search *internalConcordancesAPI) Endpoint() string {
return search.endpoint
}
const ftBrandUUID = "dbb0bdae-1f0c-11e4-b0cb-b2227cce2b54"
func (search *internalConcordancesAPI) GTG() error {
tid := tidUtils.NewTransactionID()
ctx := tidUtils.TransactionAwareContext(context.Background(), tid)
_, err := search.searchConceptBatch(ctx, []string{ftBrandUUID})
if err != nil {
log.WithError(err).WithField(tidUtils.TransactionIDKey, tid).Error("Concept search API is not good-to-go")
}
return err
}