Skip to content

Commit

Permalink
Fix #126 add UsageInfo in PagingResult; support v3.3 rate limiting (
Browse files Browse the repository at this point in the history
#127)

* fix #126 add `UsageInfo` in `PagingResult`; support v3.3 rate limiting

* update changelog and readme
  • Loading branch information
huandu authored Jun 11, 2019
1 parent 2d3753b commit f4f8f38
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 24 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change Log #

## v2.4.0 ##

* `[NEW]` [#126](https://github.com/huandu/facebook/pull/126) Support v3.3 rate limiting header `x-business-use-case-usage` and `x-ad-account-usage` in `UsageInfo`. Use it in any `Result` by calling `Result#UsageInfo()`. It's also available in `PagingResult` by calling `PagingResult#UsageInfo()`. Thanks [@OwlLaboratory](https://github.com/OwlLaboratory) for raising this change for me.

## v2.3.3 ##

* `[FIX]` [#120](https://github.com/huandu/facebook/pull/120) Use timing-safe `hmac.Equal` to check signed request. Thanks, [@gstvg](https://github.com/gstvg).
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,14 +347,16 @@ fmt.Println("facebook api version:", debugInfo.FacebookApiVersion)

### Monitoring API usage info ###

Call `Result#UsageInfo` to get a `UsageInfo` struct containing both app and page level rate limit information from the result. More information about rate limiting can be found [here](https://developers.facebook.com/docs/graph-api/advanced/rate-limiting).
Call `Result#UsageInfo` to get a `UsageInfo` struct containing both app and page level rate limit information from the result. More information about rate limiting can be found [here](https://developers.facebook.com/docs/graph-api/overview/rate-limiting).

```go
res, _ := fb.Get("/me", fb.Params{"access_token": "xxx"})
usageInfo := res.UsageInfo()

fmt.Println("App level rate limit information:", usageInfo.App)
fmt.Println("Page level rate limit information:", usageInfo.Page)
fmt.Println("Ad account rate limiting information:", usageInfo.AdAccount)
fmt.Println("Business use case usage information:", usageInfo.BusinessUseCase)
```

### Work with package `golang.org/x/oauth2` ##
Expand Down
22 changes: 20 additions & 2 deletions paging_result.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,18 @@ import (
"net/http"
)

// PagingResult represents facebook API call result with paging information.
type PagingResult struct {
session *Session
paging pagingData
previous string
next string
}

type pagingData struct {
Data []Result `facebook:",required"`
Paging *pagingNavigator
Data []Result `facebook:",required"`
Paging *pagingNavigator
UsageInfo *UsageInfo
}

type pagingNavigator struct {
Expand Down Expand Up @@ -52,6 +61,12 @@ func (pr *PagingResult) Data() []Result {
return pr.paging.Data
}

// UsageInfo returns API usage information, including
// business use case, app, page, ad account rate limiting.
func (pr *PagingResult) UsageInfo() *UsageInfo {
return pr.paging.UsageInfo
}

// Decode decodes the current full result to a struct. See Result#Decode.
func (pr *PagingResult) Decode(v interface{}) (err error) {
res := Result{
Expand Down Expand Up @@ -127,13 +142,16 @@ func (pr *PagingResult) navigate(url *string) (noMore bool, err error) {
pr.paging.Paging.Next = ""
pr.paging.Paging.Previous = ""
}

paging := &pr.paging
err = res.Decode(paging)

if err != nil {
return
}

paging.UsageInfo = res.UsageInfo()

if paging.Paging == nil || len(paging.Data) == 0 {
*url = ""
noMore = true
Expand Down
42 changes: 23 additions & 19 deletions result.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,6 @@ var (
// Result is Facebook API call result.
type Result map[string]interface{}

// PagingResult represents facebook API call result with paging information.
type PagingResult struct {
session *Session
paging pagingData
previous string
next string
}

// BatchResult represents facebook batch API call result.
// See https://developers.facebook.com/docs/graph-api/making-multiple-requests/#multiple_methods.
type BatchResult struct {
Expand All @@ -86,18 +78,29 @@ type DebugInfo struct {

// UsageInfo is the app usage (rate limit) information returned by facebook when rate limits are possible.
type UsageInfo struct {
App struct {
CallCount int `json:"call_count"`
TotalTime int `json:"total_time"`
TotalCPUTime int `json:"total_cputime"`
} `json:"app"`
Page struct {
CallCount int `json:"call_count"`
TotalTime int `json:"total_time"`
TotalCPUTime int `json:"total_cputime"`
} `json:"page"`
App RateLimiting `json:"app"` // HTTP header X-App-Usage.
Page RateLimiting `json:"page"` // HTTP header X-Page-Usage.
AdAccount RateLimiting `json:"ad_account"` // HTTP header X-Ad-Account-Usage.
BusinessUseCase BusinessUseCaseUsage `json:"business_use_case"` // HTTP header x-business-use-case-usage.
}

// RateLimiting is the rate limiting header for business use cases.
type RateLimiting struct {
CallCount int `json:"call_count"` // Percentage of calls made for this business ad account.
TotalTime int `json:"total_time"` // Percentage of the total CPU time that has been used.
TotalCPUTime int `json:"total_cputime"` // Percentage of the total time that has been used.
Type string `json:"type"` // Type of rate limit logic being applied.
EstimatedTimeToRegainAccess int `json:"estimated_time_to_regain_access"` // Time in minutes to resume calls.
}

// AdAccountUsage is the rate limiting header for Ads API.
type AdAccountUsage struct {
AccIDUtilPCT float64 `json:"acc_id_util_pct"` // Percentage of calls made for this ad account.
}

// BusinessUseCaseUsage
type BusinessUseCaseUsage map[string][]*RateLimiting

// DebugMessage is one debug message in "__debug__" of graph API response.
type DebugMessage struct {
Type string
Expand Down Expand Up @@ -449,7 +452,8 @@ func (res Result) DebugInfo() *DebugInfo {
return debugInfo
}

// UsageInfo returns app and page usage info (rate limits)
// UsageInfo returns API usage information, including
// business use case, app, page, ad account rate limiting.
func (res Result) UsageInfo() *UsageInfo {
if usageInfo, ok := res[usageInfoKey]; ok {
if usage, ok := usageInfo.(*UsageInfo); ok {
Expand Down
13 changes: 11 additions & 2 deletions session.go
Original file line number Diff line number Diff line change
Expand Up @@ -625,15 +625,24 @@ func (session *Session) addUsageInfo(res Result, response *http.Response) Result
}

var usageInfo UsageInfo
header := response.Header

if usage := response.Header.Get("X-App-Usage"); usage != "" {
if usage := header.Get("X-App-Usage"); usage != "" {
json.Unmarshal([]byte(usage), &usageInfo.App)
}

if usage := response.Header.Get("X-Page-Usage"); usage != "" {
if usage := header.Get("X-Page-Usage"); usage != "" {
json.Unmarshal([]byte(usage), &usageInfo.Page)
}

if usage := header.Get("X-Ad-Account-Usage"); usage != "" {
json.Unmarshal([]byte(usage), &usageInfo.AdAccount)
}

if usage := header.Get("X-Business-Use-Case-Usage"); usage != "" {
json.Unmarshal([]byte(usage), &usageInfo.BusinessUseCase)
}

res[usageInfoKey] = &usageInfo
return res
}
Expand Down

0 comments on commit f4f8f38

Please sign in to comment.