Skip to content

Commit

Permalink
Update product cache to use /entitlements endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
dinoboy197 committed Jan 5, 2024
1 parent 29c2f7b commit e2fbe18
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 61 deletions.
91 changes: 59 additions & 32 deletions pkg/api/product_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,20 @@ const (
// Products is the slice of available products supported by real-time stats.
var Products = []string{ProductDefault, ProductOriginInspector, ProductDomainInspector}

// Product models the response from the Fastly Product Entitlement API.
type Product struct {
HasAccess bool `json:"has_access"`
Product struct {
Name string `json:"id"`
} `json:"product"`
type response struct {
Customers *[]customer `json:"customers,omitempty"`
}

type customer struct {
Contracts *[]contract `json:"contracts,omitempty"`
}

type contract struct {
Items *[]item `json:"items,omitempty"`
}

type item struct {
ProductID *string `json:"product_id,omitempty"`
}

// ProductCache fetches product information from the Fastly Product Entitlement API
Expand All @@ -59,41 +67,60 @@ func NewProductCache(client HTTPClient, token string, logger log.Logger) *Produc

// Refresh requests data from the Fastly API and stores data in the cache.
func (p *ProductCache) Refresh(ctx context.Context) error {
for _, product := range Products {
if product == ProductDefault {
continue
}
uri := fmt.Sprintf("https://api.fastly.com/entitled-products/%s", product)

req, err := http.NewRequestWithContext(ctx, "GET", uri, nil)
if err != nil {
return fmt.Errorf("error constructing API product request: %w", err)
}
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.fastly.com/entitlements", nil)
if err != nil {
return fmt.Errorf("error constructing API product request: %w", err)
}

req.Header.Set("Fastly-Key", p.token)
req.Header.Set("Accept", "application/json")
resp, err := p.client.Do(req)
if err != nil {
return fmt.Errorf("error executing API product request: %w", err)
}
defer resp.Body.Close()
req.Header.Set("Fastly-Key", p.token)
req.Header.Set("Accept", "application/json")
resp, err := p.client.Do(req)
if err != nil {
return fmt.Errorf("error executing API product request: %w", err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return NewError(resp)
}
if resp.StatusCode != http.StatusOK {
return NewError(resp)
}

var response Product
var response response

if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
return fmt.Errorf("error decoding API product response: %w", err)
}
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
return fmt.Errorf("error decoding API product response: %w", err)
}

level.Debug(p.logger).Log("product", response.Product.Name, "hasAccess", response.HasAccess)
activeProducts := make(map[string]interface{})

if response.Customers != nil {
for _, customer := range *response.Customers {
if customer.Contracts == nil {
continue
}
for _, contract := range *customer.Contracts {
if contract.Items == nil {
continue
}
for _, item := range *contract.Items {
if item.ProductID == nil {
continue
}
activeProducts[*item.ProductID] = true
}
}
}
}

for _, product := range Products {
if product == ProductDefault {
continue
}
_, hasAccess := activeProducts[product]
level.Debug(p.logger).Log("product", product, "hasAccess", hasAccess)
p.mtx.Lock()
p.products[response.Product.Name] = response.HasAccess
p.products[product] = hasAccess
p.mtx.Unlock()

}

return nil
Expand Down
52 changes: 23 additions & 29 deletions pkg/api/product_cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func TestProductCache(t *testing.T) {
}{
{
name: "success",
client: newSequentialResponseClient(productsResponseOne, productsResponseTwo),
client: newSequentialResponseClient(productsResponse),
wantErr: nil,
wantProds: map[string]bool{
"origin_inspector": true,
Expand Down Expand Up @@ -55,34 +55,28 @@ func TestProductCache(t *testing.T) {
}
}

const productsResponseOne = `
// only products that the customer is entitled to are returned from the /entitlements endpoint
const productsResponse = `
{
"product": {
"id": "origin_inspector",
"object": "product"
},
"has_access": true,
"access_level": "Origin_Inspector",
"has_permission_to_enable": false,
"has_permission_to_disable": true,
"_links": {
"self": ""
}
}
`

const productsResponseTwo = `
{
"product": {
"id": "domain_inspector",
"object": "product"
},
"has_access": false,
"access_level": "Domain_Inspector",
"has_permission_to_enable": false,
"has_permission_to_disable": true,
"_links": {
"self": ""
}
"customers": [
{
"contracts": [
{
"items": [
{
"product_id": "origin_inspector"
},
{
"other_key": "other"
}
]
},
{
"other_key_2": [
]
}
]
}
]
}
`

0 comments on commit e2fbe18

Please sign in to comment.