updating make
Этот коммит содержится в:
13
Godeps/Godeps.json
сгенерированный
13
Godeps/Godeps.json
сгенерированный
@@ -88,11 +88,6 @@
|
|||||||
"ImportPath": "github.com/gorilla/websocket",
|
"ImportPath": "github.com/gorilla/websocket",
|
||||||
"Rev": "6fd0f867fef40c540fa05c59f86396de10a632a6"
|
"Rev": "6fd0f867fef40c540fa05c59f86396de10a632a6"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"ImportPath": "github.com/huandu/facebook",
|
|
||||||
"Comment": "v1.5.2",
|
|
||||||
"Rev": "7c46d89211b9a3f01087f47f6a399b815801ade4"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mssola/user_agent",
|
"ImportPath": "github.com/mssola/user_agent",
|
||||||
"Comment": "v0.4.1-2-g35c7f18",
|
"Comment": "v0.4.1-2-g35c7f18",
|
||||||
@@ -122,6 +117,10 @@
|
|||||||
"ImportPath": "golang.org/x/crypto/blowfish",
|
"ImportPath": "golang.org/x/crypto/blowfish",
|
||||||
"Rev": "74f810a0152f4c50a16195f6b9ff44afc35594e8"
|
"Rev": "74f810a0152f4c50a16195f6b9ff44afc35594e8"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/image/bmp",
|
||||||
|
"Rev": "eb11b45157c1b71f30b3cec66306f1cd779a689e"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "gopkg.in/bufio.v1",
|
"ImportPath": "gopkg.in/bufio.v1",
|
||||||
"Comment": "v1",
|
"Comment": "v1",
|
||||||
@@ -136,10 +135,6 @@
|
|||||||
"ImportPath": "gopkg.in/redis.v2",
|
"ImportPath": "gopkg.in/redis.v2",
|
||||||
"Comment": "v2.3.2",
|
"Comment": "v2.3.2",
|
||||||
"Rev": "e6179049628164864e6e84e973cfb56335748dea"
|
"Rev": "e6179049628164864e6e84e973cfb56335748dea"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/image/bmp",
|
|
||||||
"Rev": "eb11b45157c1b71f30b3cec66306f1cd779a689e"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
47
Godeps/_workspace/src/github.com/huandu/facebook/CHANGELOG.md
сгенерированный
поставляемый
47
Godeps/_workspace/src/github.com/huandu/facebook/CHANGELOG.md
сгенерированный
поставляемый
@@ -1,47 +0,0 @@
|
|||||||
# Change Log #
|
|
||||||
|
|
||||||
## v1.5.2 ##
|
|
||||||
|
|
||||||
* `[FIX]` [#32](https://github.com/huandu/facebook/pull/32) BatchApi/Batch returns facebook error when access token is not valid.
|
|
||||||
|
|
||||||
## v1.5.1 ##
|
|
||||||
|
|
||||||
* `[FIX]` [#31](https://github.com/huandu/facebook/pull/31) When `/oauth/access_token` returns a query string instead of json, this package can correctly handle it.
|
|
||||||
|
|
||||||
## v1.5.0 ##
|
|
||||||
|
|
||||||
* `[NEW]` [#28](https://github.com/huandu/facebook/pull/28) Support debug mode introduced by facebook graph API v2.3.
|
|
||||||
* `[FIX]` Removed all test cases depending on facebook graph API v1.0.
|
|
||||||
|
|
||||||
## v1.4.1 ##
|
|
||||||
|
|
||||||
* `[NEW]` [#27](https://github.com/huandu/facebook/pull/27) Timestamp value in Graph API response can be decoded as a `time.Time` value now. Thanks, [@Lazyshot](https://github.com/Lazyshot).
|
|
||||||
|
|
||||||
## v1.4.0 ##
|
|
||||||
|
|
||||||
* `[FIX]` [#23](https://github.com/huandu/facebook/issues/24) Algorithm change: Camel case string to underscore string supports abbreviation
|
|
||||||
|
|
||||||
Fix for [#23](https://github.com/huandu/facebook/issues/24) could be a breaking change. Camel case string `HTTPServer` will be converted to `http_server` instead of `h_t_t_p_server`. See issue description for detail.
|
|
||||||
|
|
||||||
## v1.3.0 ##
|
|
||||||
|
|
||||||
* `[NEW]` [#22](https://github.com/huandu/facebook/issues/22) Add a new helper struct `BatchResult` to hold batch request responses.
|
|
||||||
|
|
||||||
## v1.2.0 ##
|
|
||||||
|
|
||||||
* `[NEW]` [#20](https://github.com/huandu/facebook/issues/20) Add Decode functionality for paging results. Thanks, [@cbroglie](https://github.com/cbroglie).
|
|
||||||
* `[FIX]` [#21](https://github.com/huandu/facebook/issues/21) `Session#Inspect` cannot return error if access token is invalid.
|
|
||||||
|
|
||||||
Fix for [#21](https://github.com/huandu/facebook/issues/21) will result a possible breaking change in `Session#Inspect`. It was return whole result returned by facebook inspect api. Now it only return its "data" sub-tree. As facebook puts everything including error message in "data" sub-tree, I believe it's reasonable to make this change.
|
|
||||||
|
|
||||||
## v1.1.0 ##
|
|
||||||
|
|
||||||
* `[FIX]` [#19](https://github.com/huandu/facebook/issues/19) Any valid int64 number larger than 2^53 or smaller than -2^53 can be correctly decoded without precision lost.
|
|
||||||
|
|
||||||
Fix for [#19](https://github.com/huandu/facebook/issues/19) will result a possible breaking change in `Result#Get` and `Result#GetField`. If a JSON field is a number, these two functions will return json.Number instead of float64.
|
|
||||||
|
|
||||||
The fix also introduces a side effect in `Result#Decode` and `Result#DecodeField`. A number field (`int*` and `float*`) can be decoded to a string. It was not allowed in previous version.
|
|
||||||
|
|
||||||
## v1.0.0 ##
|
|
||||||
|
|
||||||
Initial tag. Library is stable enough for all features mentioned in README.md.
|
|
||||||
7
Godeps/_workspace/src/github.com/huandu/facebook/CONTRIBUTING.md
сгенерированный
поставляемый
7
Godeps/_workspace/src/github.com/huandu/facebook/CONTRIBUTING.md
сгенерированный
поставляемый
@@ -1,7 +0,0 @@
|
|||||||
Thanks for contributing this project!
|
|
||||||
|
|
||||||
Please don't forget to use `gofmt` to make your code look good.
|
|
||||||
|
|
||||||
Here is the command I use. Please always use the same parameters.
|
|
||||||
|
|
||||||
go fmt
|
|
||||||
19
Godeps/_workspace/src/github.com/huandu/facebook/LICENSE
сгенерированный
поставляемый
19
Godeps/_workspace/src/github.com/huandu/facebook/LICENSE
сгенерированный
поставляемый
@@ -1,19 +0,0 @@
|
|||||||
Copyright (c) 2012 - 2015 Huan Du
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
347
Godeps/_workspace/src/github.com/huandu/facebook/README.md
сгенерированный
поставляемый
347
Godeps/_workspace/src/github.com/huandu/facebook/README.md
сгенерированный
поставляемый
@@ -1,347 +0,0 @@
|
|||||||
# A Facebook Graph API SDK In Golang #
|
|
||||||
|
|
||||||
[](https://travis-ci.org/huandu/facebook)
|
|
||||||
|
|
||||||
This is a Go package fully supports Facebook Graph API with file upload, batch request, FQL and multi-FQL. It can be used in Google App Engine.
|
|
||||||
|
|
||||||
API documents can be found on [godoc](http://godoc.org/github.com/huandu/facebook).
|
|
||||||
|
|
||||||
Feel free to create an issue or send me a pull request if you have any "how-to" question or bug or suggestion when using this package. I'll try my best to reply it.
|
|
||||||
|
|
||||||
## Get It ##
|
|
||||||
|
|
||||||
Use `go get -u github.com/huandu/facebook` to get or update it.
|
|
||||||
|
|
||||||
## Usage ##
|
|
||||||
|
|
||||||
### Quick start ###
|
|
||||||
|
|
||||||
Here is a sample to read my Facebook username by uid.
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
fb "github.com/huandu/facebook"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
res, _ := fb.Get("/538744468", fb.Params{
|
|
||||||
"fields": "username",
|
|
||||||
"access_token": "a-valid-access-token",
|
|
||||||
})
|
|
||||||
fmt.Println("here is my facebook username:", res["username"])
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Type of `res` is `fb.Result` (a.k.a. `map[string]interface{}`).
|
|
||||||
This type has several useful methods to decode `res` to any Go type safely.
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Decode "username" to a Go string.
|
|
||||||
var username string
|
|
||||||
res.DecodeField("username", &username)
|
|
||||||
fmt.Println("alternative way to get username:", username)
|
|
||||||
|
|
||||||
// It's also possible to decode the whole result into a predefined struct.
|
|
||||||
type User struct {
|
|
||||||
Username string
|
|
||||||
}
|
|
||||||
|
|
||||||
var user User
|
|
||||||
res.Decode(&user)
|
|
||||||
fmt.Println("print username in struct:", user.Username)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Read a graph `user` object with a valid access token ###
|
|
||||||
|
|
||||||
```go
|
|
||||||
res, err := fb.Get("/me/feed", fb.Params{
|
|
||||||
"access_token": "a-valid-access-token",
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
// err can be an facebook API error.
|
|
||||||
// if so, the Error struct contains error details.
|
|
||||||
if e, ok := err.(*Error); ok {
|
|
||||||
fmt.Logf("facebook error. [message:%v] [type:%v] [code:%v] [subcode:%v]",
|
|
||||||
e.Message, e.Type, e.Code, e.ErrorSubcode)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// read my last feed.
|
|
||||||
fmt.Println("my latest feed story is:", res.Get("data.0.story"))
|
|
||||||
```
|
|
||||||
|
|
||||||
### Read a graph `search` for page and decode slice of maps
|
|
||||||
|
|
||||||
```go
|
|
||||||
res, _ := fb.Get("/search", fb.Params{
|
|
||||||
"access_token": "a-valid-access-token",
|
|
||||||
"type": "page",
|
|
||||||
"q": "nightlife,singapore",
|
|
||||||
})
|
|
||||||
|
|
||||||
var items []fb.Result
|
|
||||||
|
|
||||||
err := res.DecodeField("data", &items)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Logf("An error has happened %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, item := range items {
|
|
||||||
fmt.Println(item["id"])
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Use `App` and `Session` ###
|
|
||||||
|
|
||||||
It's recommended to use `App` and `Session` in a production app. They provide more controls over all API calls. They can also make code clear and concise.
|
|
||||||
|
|
||||||
```go
|
|
||||||
// create a global App var to hold app id and secret.
|
|
||||||
var globalApp = fb.New("your-app-id", "your-app-secret")
|
|
||||||
|
|
||||||
// facebook asks for a valid redirect uri when parsing signed request.
|
|
||||||
// it's a new enforced policy starting in late 2013.
|
|
||||||
globalApp.RedirectUri = "http://your.site/canvas/url/"
|
|
||||||
|
|
||||||
// here comes a client with a facebook signed request string in query string.
|
|
||||||
// creates a new session with signed request.
|
|
||||||
session, _ := globalApp.SessionFromSignedRequest(signedRequest)
|
|
||||||
|
|
||||||
// if there is another way to get decoded access token,
|
|
||||||
// creates a session directly with the token.
|
|
||||||
session := globalApp.Session(token)
|
|
||||||
|
|
||||||
// validate access token. err is nil if token is valid.
|
|
||||||
err := session.Validate()
|
|
||||||
|
|
||||||
// use session to send api request with access token.
|
|
||||||
res, _ := session.Get("/me/feed", nil)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Use `paging` field in response. ###
|
|
||||||
|
|
||||||
Some Graph API responses use a special JSON structure to provide paging information. Use `Result.Paging()` to walk through all data in such results.
|
|
||||||
|
|
||||||
```go
|
|
||||||
res, _ := session.Get("/me/home", nil)
|
|
||||||
|
|
||||||
// create a paging structure.
|
|
||||||
paging, _ := res.Paging(session)
|
|
||||||
|
|
||||||
// get current results.
|
|
||||||
results := paging.Data()
|
|
||||||
|
|
||||||
// get next page.
|
|
||||||
noMore, err := paging.Next()
|
|
||||||
results = paging.Data()
|
|
||||||
```
|
|
||||||
|
|
||||||
### Read graph api response and decode result into a struct ###
|
|
||||||
|
|
||||||
As facebook Graph API always uses lower case words as keys in API response.
|
|
||||||
This package can convert go's camel-case-style struct field name to facebook's underscore-style API key name.
|
|
||||||
|
|
||||||
For instance, to decode following JSON response...
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"foo_bar": "player"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
One can use following struct.
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Data struct {
|
|
||||||
FooBar string // "FooBar" maps to "foo_bar" in JSON automatically in this case.
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Decoding behavior can be changed per field through field tag -- just like what `encoding/json` does.
|
|
||||||
|
|
||||||
Following is a sample shows all possible field tags.
|
|
||||||
|
|
||||||
```go
|
|
||||||
// define a facebook feed object.
|
|
||||||
type FacebookFeed struct {
|
|
||||||
Id string `facebook:",required"` // this field must exist in response.
|
|
||||||
// mind the "," before "required".
|
|
||||||
Story string
|
|
||||||
FeedFrom *FacebookFeedFrom `facebook:"from"` // use customized field name "from"
|
|
||||||
CreatedTime string `facebook:"created_time,required"` // both customized field name and "required" flag.
|
|
||||||
}
|
|
||||||
|
|
||||||
type FacebookFeedFrom struct {
|
|
||||||
Name, Id string
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a feed object direct from graph api result.
|
|
||||||
var feed FacebookFeed
|
|
||||||
res, _ := session.Get("/me/feed", nil)
|
|
||||||
res.DecodeField("data.0", &feed) // read latest feed
|
|
||||||
```
|
|
||||||
|
|
||||||
### Send a batch request ###
|
|
||||||
|
|
||||||
```go
|
|
||||||
params1 := Params{
|
|
||||||
"method": fb.GET,
|
|
||||||
"relative_url": "me",
|
|
||||||
}
|
|
||||||
params2 := Params{
|
|
||||||
"method": fb.GET,
|
|
||||||
"relative_url": uint64(100002828925788),
|
|
||||||
}
|
|
||||||
results, err := fb.BatchApi(your_access_token, params1, params2)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
// check error...
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// batchResult1 and batchResult2 are response for params1 and params2.
|
|
||||||
batchResult1, _ := results[0].Batch()
|
|
||||||
batchResult2, _ := results[1].Batch()
|
|
||||||
|
|
||||||
// Use parsed result.
|
|
||||||
var id string
|
|
||||||
res := batchResult1.Result
|
|
||||||
res.DecodeField("id", &id)
|
|
||||||
|
|
||||||
// Use response header.
|
|
||||||
contentType := batchResult1.Header.Get("Content-Type")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Send FQL query ###
|
|
||||||
|
|
||||||
```go
|
|
||||||
results, _ := fb.FQL("SELECT username FROM page WHERE page_id = 20531316728")
|
|
||||||
fmt.Println(results[0]["username"]) // print "facebook"
|
|
||||||
|
|
||||||
// most FQL query requires access token. create session to hold access token.
|
|
||||||
session := &fb.Session{}
|
|
||||||
session.SetAccessToken("A-VALID-ACCESS-TOKEN")
|
|
||||||
results, _ := session.FQL("SELECT username FROM page WHERE page_id = 20531316728")
|
|
||||||
fmt.Println(results[0]["username"]) // print "facebook"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Make multi-FQL ###
|
|
||||||
|
|
||||||
```go
|
|
||||||
res, _ := fb.MultiFQL(Params{
|
|
||||||
"query1": "SELECT username FROM page WHERE page_id = 20531316728",
|
|
||||||
"query2": "SELECT uid FROM user WHERE uid = 538744468",
|
|
||||||
})
|
|
||||||
var query1, query2 []Result
|
|
||||||
|
|
||||||
// get response for query1 and query2.
|
|
||||||
res.DecodeField("query1", &query1)
|
|
||||||
res.DecodeField("query2", &query2)
|
|
||||||
|
|
||||||
// most FQL query requires access token. create session to hold access token.
|
|
||||||
session := &fb.Session{}
|
|
||||||
session.SetAccessToken("A-VALID-ACCESS-TOKEN")
|
|
||||||
res, _ := session.MultiFQL(Params{
|
|
||||||
"query1": "...",
|
|
||||||
"query2": "...",
|
|
||||||
})
|
|
||||||
|
|
||||||
// same as the sample without access token...
|
|
||||||
```
|
|
||||||
|
|
||||||
### Use it in Google App Engine ###
|
|
||||||
|
|
||||||
Google App Engine provide `appengine/urlfetch` package as standard http client package. Default client in `net/http` doesn't work. One must explicitly set http client in `Session` to make it work.
|
|
||||||
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
"appengine"
|
|
||||||
"appengine/urlfetch"
|
|
||||||
)
|
|
||||||
|
|
||||||
// suppose it's the appengine context initialized somewhere.
|
|
||||||
var context appengine.Context
|
|
||||||
|
|
||||||
// default Session object uses http.DefaultClient which is not allowed to use
|
|
||||||
// in appengine. one has to create a Session and assign it a special client.
|
|
||||||
seesion := globalApp.Session("a-access-token")
|
|
||||||
session.HttpClient = urlfetch.Client(context)
|
|
||||||
|
|
||||||
// now, session uses appengine http client now.
|
|
||||||
res, err := session.Get("/me", nil)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Select Graph API version ###
|
|
||||||
|
|
||||||
See [Platform Versioning](https://developers.facebook.com/docs/apps/versions) to understand facebook versioning strategy.
|
|
||||||
|
|
||||||
```go
|
|
||||||
// this package uses default version which is controlled by facebook app setting.
|
|
||||||
// change following global variable to specific a global default version.
|
|
||||||
fb.Version = "v2.0"
|
|
||||||
|
|
||||||
// starting with graph api v2.0, it's not allowed to get user information without access token.
|
|
||||||
fb.Api("huan.du", GET, nil)
|
|
||||||
|
|
||||||
// it's possible to specify version per session.
|
|
||||||
session := &fb.Session{}
|
|
||||||
session.Version = "v2.0" // overwrite global default.
|
|
||||||
```
|
|
||||||
|
|
||||||
### Enable `appsecret_proof` ###
|
|
||||||
|
|
||||||
Facebook can verify Graph API Calls with `appsecret_proof`. It's a feature to make Graph API call more secure. See [Securing Graph API Requests](https://developers.facebook.com/docs/graph-api/securing-requests) to know more about it.
|
|
||||||
|
|
||||||
```go
|
|
||||||
globalApp := fb.New("your-app-id", "your-app-secret")
|
|
||||||
|
|
||||||
// enable "appsecret_proof" for all sessions created by this app.
|
|
||||||
globalApp.EnableAppsecretProof = true
|
|
||||||
|
|
||||||
// all calls in this session are secured.
|
|
||||||
session := globalApp.Session("a-valid-access-token")
|
|
||||||
session.Get("/me", nil)
|
|
||||||
|
|
||||||
// it's also possible to enable/disable this feature per session.
|
|
||||||
session.EnableAppsecretProof(false)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Debugging API Requests ###
|
|
||||||
|
|
||||||
Facebook introduces a way to debug graph API calls. See [Debugging API Requests](https://developers.facebook.com/docs/graph-api/using-graph-api/v2.3#debugging) for details.
|
|
||||||
|
|
||||||
This package provides both package level and per session debug flag. Set `Debug` to a `DEBUG_*` constant to change debug mode globally; or use `Session#SetDebug` to change debug mode for one session.
|
|
||||||
|
|
||||||
When debug mode is turned on, use `Result#DebugInfo` to get `DebugInfo` struct from result.
|
|
||||||
|
|
||||||
```go
|
|
||||||
fb.Debug = fb.DEBUG_ALL
|
|
||||||
|
|
||||||
res, _ := fb.Get("/me", fb.Params{"access_token": "xxx"})
|
|
||||||
debugInfo := res.DebugInfo()
|
|
||||||
|
|
||||||
fmt.Println("http headers:", debugInfo.Header)
|
|
||||||
fmt.Println("facebook api version:", debugInfo.FacebookApiVersion)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Change Log ##
|
|
||||||
|
|
||||||
See [CHANGELOG.md](CHANGELOG.md).
|
|
||||||
|
|
||||||
## Out of Scope ##
|
|
||||||
|
|
||||||
1. No OAuth integration. This package only provides APIs to parse/verify access token and code generated in OAuth 2.0 authentication process.
|
|
||||||
2. No old RESTful API support. Such APIs are deprecated for years. Forget about them.
|
|
||||||
|
|
||||||
## License ##
|
|
||||||
|
|
||||||
This package is licensed under MIT license. See LICENSE for details.
|
|
||||||
180
Godeps/_workspace/src/github.com/huandu/facebook/api.go
сгенерированный
поставляемый
180
Godeps/_workspace/src/github.com/huandu/facebook/api.go
сгенерированный
поставляемый
@@ -1,180 +0,0 @@
|
|||||||
// A facebook graph api client in go.
|
|
||||||
// https://github.com/huandu/facebook/
|
|
||||||
//
|
|
||||||
// Copyright 2012 - 2015, Huan Du
|
|
||||||
// Licensed under the MIT license
|
|
||||||
// https://github.com/huandu/facebook/blob/master/LICENSE
|
|
||||||
|
|
||||||
// This is a Go library fully supports Facebook Graph API (both 1.0 and 2.x) with
|
|
||||||
// file upload, batch request, FQL and multi-FQL. It can be used in Google App Engine.
|
|
||||||
//
|
|
||||||
// Library design is highly influenced by facebook official PHP/JS SDK.
|
|
||||||
// If you have experience with PHP/JS SDK, you may feel quite familiar with it.
|
|
||||||
//
|
|
||||||
// Go to project home page to see samples. Link: https://github.com/huandu/facebook
|
|
||||||
//
|
|
||||||
// This library doesn't implement any deprecated old RESTful API. And it won't.
|
|
||||||
package facebook
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// Default facebook api version.
|
|
||||||
// It can be any valid version string (e.g. "v2.3") or empty.
|
|
||||||
//
|
|
||||||
// See https://developers.facebook.com/docs/apps/versions for details.
|
|
||||||
Version string
|
|
||||||
|
|
||||||
// Set app level debug mode.
|
|
||||||
// After setting DebugMode, all newly created session will use the mode
|
|
||||||
// to communicate with graph API.
|
|
||||||
//
|
|
||||||
// See https://developers.facebook.com/docs/graph-api/using-graph-api/v2.3#debugging
|
|
||||||
Debug DebugMode
|
|
||||||
)
|
|
||||||
|
|
||||||
// Makes a facebook graph api call with default session.
|
|
||||||
//
|
|
||||||
// Method can be GET, POST, DELETE or PUT.
|
|
||||||
//
|
|
||||||
// Params represents query strings in this call.
|
|
||||||
// Keys and values in params will be encoded for URL automatically. So there is
|
|
||||||
// no need to encode keys or values in params manually. Params can be nil.
|
|
||||||
//
|
|
||||||
// If you want to get
|
|
||||||
// https://graph.facebook.com/huandu?fields=name,username
|
|
||||||
// Api should be called as following
|
|
||||||
// Api("/huandu", GET, Params{"fields": "name,username"})
|
|
||||||
// or in a simplified way
|
|
||||||
// Get("/huandu", Params{"fields": "name,username"})
|
|
||||||
//
|
|
||||||
// Api is a wrapper of Session.Api(). It's designed for graph api that doesn't require
|
|
||||||
// app id, app secret and access token. It can be called in multiple goroutines.
|
|
||||||
//
|
|
||||||
// If app id, app secret or access token is required in graph api, caller should
|
|
||||||
// create a new facebook session through App instance instead.
|
|
||||||
func Api(path string, method Method, params Params) (Result, error) {
|
|
||||||
return defaultSession.Api(path, method, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get is a short hand of Api(path, GET, params).
|
|
||||||
func Get(path string, params Params) (Result, error) {
|
|
||||||
return Api(path, GET, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post is a short hand of Api(path, POST, params).
|
|
||||||
func Post(path string, params Params) (Result, error) {
|
|
||||||
return Api(path, POST, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete is a short hand of Api(path, DELETE, params).
|
|
||||||
func Delete(path string, params Params) (Result, error) {
|
|
||||||
return Api(path, DELETE, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put is a short hand of Api(path, PUT, params).
|
|
||||||
func Put(path string, params Params) (Result, error) {
|
|
||||||
return Api(path, PUT, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Makes a batch facebook graph api call with default session.
|
|
||||||
//
|
|
||||||
// BatchApi supports most kinds of batch calls defines in facebook batch api document,
|
|
||||||
// except uploading binary data. Use Batch to do so.
|
|
||||||
//
|
|
||||||
// Note: API response is stored in "body" field of a Result.
|
|
||||||
// results, _ := BatchApi(accessToken, Params{...}, Params{...})
|
|
||||||
//
|
|
||||||
// // Use first batch api response.
|
|
||||||
// var res1 *BatchResult
|
|
||||||
// var err error
|
|
||||||
// res1, err = results[0].Batch()
|
|
||||||
//
|
|
||||||
// if err != nil {
|
|
||||||
// // this is not a valid batch api response.
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Use BatchResult#Result to get response body content as Result.
|
|
||||||
// res := res1.Result
|
|
||||||
//
|
|
||||||
// Facebook document: https://developers.facebook.com/docs/graph-api/making-multiple-requests
|
|
||||||
func BatchApi(accessToken string, params ...Params) ([]Result, error) {
|
|
||||||
return Batch(Params{"access_token": accessToken}, params...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Makes a batch facebook graph api call with default session.
|
|
||||||
// Batch is designed for more advanced usage including uploading binary files.
|
|
||||||
//
|
|
||||||
// An uploading files sample
|
|
||||||
// // equivalent to following curl command (borrowed from facebook docs)
|
|
||||||
// // curl \
|
|
||||||
// // -F 'access_token=…' \
|
|
||||||
// // -F 'batch=[{"method":"POST","relative_url":"me/photos","body":"message=My cat photo","attached_files":"file1"},{"method":"POST","relative_url":"me/photos","body":"message=My dog photo","attached_files":"file2"},]' \
|
|
||||||
// // -F 'file1=@cat.gif' \
|
|
||||||
// // -F 'file2=@dog.jpg' \
|
|
||||||
// // https://graph.facebook.com
|
|
||||||
// Batch(Params{
|
|
||||||
// "access_token": "the-access-token",
|
|
||||||
// "file1": File("cat.gif"),
|
|
||||||
// "file2": File("dog.jpg"),
|
|
||||||
// }, Params{
|
|
||||||
// "method": "POST",
|
|
||||||
// "relative_url": "me/photos",
|
|
||||||
// "body": "message=My cat photo",
|
|
||||||
// "attached_files": "file1",
|
|
||||||
// }, Params{
|
|
||||||
// "method": "POST",
|
|
||||||
// "relative_url": "me/photos",
|
|
||||||
// "body": "message=My dog photo",
|
|
||||||
// "attached_files": "file2",
|
|
||||||
// })
|
|
||||||
//
|
|
||||||
// Facebook document: https://developers.facebook.com/docs/graph-api/making-multiple-requests
|
|
||||||
func Batch(batchParams Params, params ...Params) ([]Result, error) {
|
|
||||||
return defaultSession.Batch(batchParams, params...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Makes a FQL query with default session.
|
|
||||||
// Returns a slice of Result. If there is no query result, the result is nil.
|
|
||||||
//
|
|
||||||
// FQL can only make query without "access_token". For query requiring "access_token", create
|
|
||||||
// Session and call its FQL method.
|
|
||||||
//
|
|
||||||
// Facebook document: https://developers.facebook.com/docs/technical-guides/fql#query
|
|
||||||
func FQL(query string) ([]Result, error) {
|
|
||||||
return defaultSession.FQL(query)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Makes a multi FQL query with default session.
|
|
||||||
// Returns a parsed Result. The key is the multi query key, and the value is the query result.
|
|
||||||
//
|
|
||||||
// MultiFQL can only make query without "access_token". For query requiring "access_token", create
|
|
||||||
// Session and call its MultiFQL method.
|
|
||||||
//
|
|
||||||
// See Session.MultiFQL document for samples.
|
|
||||||
//
|
|
||||||
// Facebook document: https://developers.facebook.com/docs/technical-guides/fql#multi
|
|
||||||
func MultiFQL(queries Params) (Result, error) {
|
|
||||||
return defaultSession.MultiFQL(queries)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Makes an arbitrary HTTP request with default session.
|
|
||||||
// It expects server responses a facebook Graph API response.
|
|
||||||
// request, _ := http.NewRequest("https://graph.facebook.com/538744468", "GET", nil)
|
|
||||||
// res, err := Request(request)
|
|
||||||
// fmt.Println(res["gender"]) // get "male"
|
|
||||||
func Request(request *http.Request) (Result, error) {
|
|
||||||
return defaultSession.Request(request)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultHttpClient returns the http client for default session.
|
|
||||||
func DefaultHttpClient() HttpClient {
|
|
||||||
return defaultSession.HttpClient
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetHttpClient updates the http client of default session.
|
|
||||||
func SetHttpClient(client HttpClient) {
|
|
||||||
defaultSession.HttpClient = client
|
|
||||||
}
|
|
||||||
255
Godeps/_workspace/src/github.com/huandu/facebook/app.go
сгенерированный
поставляемый
255
Godeps/_workspace/src/github.com/huandu/facebook/app.go
сгенерированный
поставляемый
@@ -1,255 +0,0 @@
|
|||||||
// A facebook graph api client in go.
|
|
||||||
// https://github.com/huandu/facebook/
|
|
||||||
//
|
|
||||||
// Copyright 2012 - 2015, Huan Du
|
|
||||||
// Licensed under the MIT license
|
|
||||||
// https://github.com/huandu/facebook/blob/master/LICENSE
|
|
||||||
|
|
||||||
package facebook
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Creates a new App and sets app id and secret.
|
|
||||||
func New(appId, appSecret string) *App {
|
|
||||||
return &App{
|
|
||||||
AppId: appId,
|
|
||||||
AppSecret: appSecret,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets application access token, useful for gathering public information about users and applications.
|
|
||||||
func (app *App) AppAccessToken() string {
|
|
||||||
return app.AppId + "|" + app.AppSecret
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parses signed request.
|
|
||||||
func (app *App) ParseSignedRequest(signedRequest string) (res Result, err error) {
|
|
||||||
strs := strings.SplitN(signedRequest, ".", 2)
|
|
||||||
|
|
||||||
if len(strs) != 2 {
|
|
||||||
err = fmt.Errorf("invalid signed request format.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sig, e1 := decodeBase64URLEncodingString(strs[0])
|
|
||||||
|
|
||||||
if e1 != nil {
|
|
||||||
err = fmt.Errorf("cannot decode signed request sig. error is %v.", e1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
payload, e2 := decodeBase64URLEncodingString(strs[1])
|
|
||||||
|
|
||||||
if e2 != nil {
|
|
||||||
err = fmt.Errorf("cannot decode signed request payload. error is %v.", e2)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(payload, &res)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("signed request payload is not a valid json string. error is %v.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var hashMethod string
|
|
||||||
err = res.DecodeField("algorithm", &hashMethod)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("signed request payload doesn't contains a valid 'algorithm' field.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
hashMethod = strings.ToUpper(hashMethod)
|
|
||||||
|
|
||||||
if hashMethod != "HMAC-SHA256" {
|
|
||||||
err = fmt.Errorf("signed request payload uses an unknown HMAC method. expect 'HMAC-SHA256'. actual '%v'.", hashMethod)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
hash := hmac.New(sha256.New, []byte(app.AppSecret))
|
|
||||||
hash.Write([]byte(strs[1])) // note: here uses the payload base64 string, not decoded bytes
|
|
||||||
expectedSig := hash.Sum(nil)
|
|
||||||
|
|
||||||
if bytes.Compare(sig, expectedSig) != 0 {
|
|
||||||
err = fmt.Errorf("bad signed request signiture.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseCode redeems code for a valid access token.
|
|
||||||
// It's a shorthand call to ParseCodeInfo(code, "").
|
|
||||||
//
|
|
||||||
// In facebook PHP SDK, there is a CSRF state to avoid attack.
|
|
||||||
// That state is not checked in this library.
|
|
||||||
// Caller is responsible to store and check state if possible.
|
|
||||||
func (app *App) ParseCode(code string) (token string, err error) {
|
|
||||||
token, _, _, err = app.ParseCodeInfo(code, "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseCodeInfo redeems code for access token and returns extra information.
|
|
||||||
// The machineId is optional.
|
|
||||||
//
|
|
||||||
// See https://developers.facebook.com/docs/facebook-login/access-tokens#extending
|
|
||||||
func (app *App) ParseCodeInfo(code, machineId string) (token string, expires int, newMachineId string, err error) {
|
|
||||||
if code == "" {
|
|
||||||
err = fmt.Errorf("code is empty")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var res Result
|
|
||||||
res, err = defaultSession.sendOauthRequest("/oauth/access_token", Params{
|
|
||||||
"client_id": app.AppId,
|
|
||||||
"redirect_uri": app.RedirectUri,
|
|
||||||
"code": code,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("cannot parse facebook response. error is %v.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = res.DecodeField("access_token", &token)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = res.DecodeField("expires_in", &expires)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := res["machine_id"]; ok {
|
|
||||||
err = res.DecodeField("machine_id", &newMachineId)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exchange a short lived access token to a long lived access token.
|
|
||||||
// Return new access token and its expires time.
|
|
||||||
func (app *App) ExchangeToken(accessToken string) (token string, expires int, err error) {
|
|
||||||
if accessToken == "" {
|
|
||||||
err = fmt.Errorf("short lived accessToken is empty")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var res Result
|
|
||||||
res, err = defaultSession.sendOauthRequest("/oauth/access_token", Params{
|
|
||||||
"grant_type": "fb_exchange_token",
|
|
||||||
"client_id": app.AppId,
|
|
||||||
"client_secret": app.AppSecret,
|
|
||||||
"fb_exchange_token": accessToken,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("cannot parse facebook response. error is %v.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = res.DecodeField("access_token", &token)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = res.DecodeField("expires_in", &expires)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get code from a long lived access token.
|
|
||||||
// Return the code retrieved from facebook.
|
|
||||||
func (app *App) GetCode(accessToken string) (code string, err error) {
|
|
||||||
if accessToken == "" {
|
|
||||||
err = fmt.Errorf("long lived accessToken is empty")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var res Result
|
|
||||||
res, err = defaultSession.sendOauthRequest("/oauth/client_code", Params{
|
|
||||||
"client_id": app.AppId,
|
|
||||||
"client_secret": app.AppSecret,
|
|
||||||
"redirect_uri": app.RedirectUri,
|
|
||||||
"access_token": accessToken,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("cannot get code from facebook. error is %v.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = res.DecodeField("code", &code)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a session based on current App setting.
|
|
||||||
func (app *App) Session(accessToken string) *Session {
|
|
||||||
return &Session{
|
|
||||||
accessToken: accessToken,
|
|
||||||
app: app,
|
|
||||||
enableAppsecretProof: app.EnableAppsecretProof,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a session from a signed request.
|
|
||||||
// If signed request contains a code, it will automatically use this code
|
|
||||||
// to exchange a valid access token.
|
|
||||||
func (app *App) SessionFromSignedRequest(signedRequest string) (session *Session, err error) {
|
|
||||||
var res Result
|
|
||||||
|
|
||||||
res, err = app.ParseSignedRequest(signedRequest)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var id, token string
|
|
||||||
|
|
||||||
res.DecodeField("user_id", &id) // it's ok without user id.
|
|
||||||
err = res.DecodeField("oauth_token", &token)
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
session = &Session{
|
|
||||||
accessToken: token,
|
|
||||||
app: app,
|
|
||||||
id: id,
|
|
||||||
enableAppsecretProof: app.EnableAppsecretProof,
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// cannot get "oauth_token"? try to get "code".
|
|
||||||
err = res.DecodeField("code", &token)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
// no code? no way to continue.
|
|
||||||
err = fmt.Errorf("cannot find 'oauth_token' and 'code'. no way to continue.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
token, err = app.ParseCode(token)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
session = &Session{
|
|
||||||
accessToken: token,
|
|
||||||
app: app,
|
|
||||||
id: id,
|
|
||||||
enableAppsecretProof: app.EnableAppsecretProof,
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
52
Godeps/_workspace/src/github.com/huandu/facebook/batch_result.go
сгенерированный
поставляемый
52
Godeps/_workspace/src/github.com/huandu/facebook/batch_result.go
сгенерированный
поставляемый
@@ -1,52 +0,0 @@
|
|||||||
// A facebook graph api client in go.
|
|
||||||
// https://github.com/huandu/facebook/
|
|
||||||
//
|
|
||||||
// Copyright 2012 - 2015, Huan Du
|
|
||||||
// Licensed under the MIT license
|
|
||||||
// https://github.com/huandu/facebook/blob/master/LICENSE
|
|
||||||
|
|
||||||
package facebook
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type batchResultHeader struct {
|
|
||||||
Name string `facebook=",required"`
|
|
||||||
Value string `facebook=",required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type batchResultData struct {
|
|
||||||
Code int `facebook=",required"`
|
|
||||||
Headers []batchResultHeader `facebook=",required"`
|
|
||||||
Body string `facebook=",required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func newBatchResult(res Result) (*BatchResult, error) {
|
|
||||||
var data batchResultData
|
|
||||||
err := res.Decode(&data)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
result := &BatchResult{
|
|
||||||
StatusCode: data.Code,
|
|
||||||
Header: http.Header{},
|
|
||||||
Body: data.Body,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal([]byte(result.Body), &result.Result)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// add headers to result.
|
|
||||||
for _, header := range data.Headers {
|
|
||||||
result.Header.Add(header.Name, header.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
74
Godeps/_workspace/src/github.com/huandu/facebook/const.go
сгенерированный
поставляемый
74
Godeps/_workspace/src/github.com/huandu/facebook/const.go
сгенерированный
поставляемый
@@ -1,74 +0,0 @@
|
|||||||
// A facebook graph api client in go.
|
|
||||||
// https://github.com/huandu/facebook/
|
|
||||||
//
|
|
||||||
// Copyright 2012 - 2015, Huan Du
|
|
||||||
// Licensed under the MIT license
|
|
||||||
// https://github.com/huandu/facebook/blob/master/LICENSE
|
|
||||||
|
|
||||||
package facebook
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Facebook graph api methods.
|
|
||||||
const (
|
|
||||||
GET Method = "GET"
|
|
||||||
POST Method = "POST"
|
|
||||||
DELETE Method = "DELETE"
|
|
||||||
PUT Method = "PUT"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
ERROR_CODE_UNKNOWN = -1 // unknown facebook graph api error code.
|
|
||||||
|
|
||||||
_MIME_FORM_URLENCODED = "application/x-www-form-urlencoded"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Graph API debug mode values.
|
|
||||||
const (
|
|
||||||
DEBUG_OFF DebugMode = "" // turn off debug.
|
|
||||||
|
|
||||||
DEBUG_ALL DebugMode = "all"
|
|
||||||
DEBUG_INFO DebugMode = "info"
|
|
||||||
DEBUG_WARNING DebugMode = "warning"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
debugInfoKey = "__debug__"
|
|
||||||
debugProtoKey = "__proto__"
|
|
||||||
debugHeaderKey = "__header__"
|
|
||||||
|
|
||||||
facebookApiVersionHeader = "facebook-api-version"
|
|
||||||
facebookDebugHeader = "x-fb-debug"
|
|
||||||
facebookRevHeader = "x-fb-rev"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// Maps aliases to Facebook domains.
|
|
||||||
// Copied from Facebook PHP SDK.
|
|
||||||
domainMap = map[string]string{
|
|
||||||
"api": "https://api.facebook.com/",
|
|
||||||
"api_video": "https://api-video.facebook.com/",
|
|
||||||
"api_read": "https://api-read.facebook.com/",
|
|
||||||
"graph": "https://graph.facebook.com/",
|
|
||||||
"graph_video": "https://graph-video.facebook.com/",
|
|
||||||
"www": "https://www.facebook.com/",
|
|
||||||
}
|
|
||||||
|
|
||||||
// checks whether it's a video post.
|
|
||||||
regexpIsVideoPost = regexp.MustCompile(`/^(\/)(.+)(\/)(videos)$/`)
|
|
||||||
|
|
||||||
// default facebook session.
|
|
||||||
defaultSession = &Session{}
|
|
||||||
|
|
||||||
typeOfPointerToBinaryData = reflect.TypeOf(&binaryData{})
|
|
||||||
typeOfPointerToBinaryFile = reflect.TypeOf(&binaryFile{})
|
|
||||||
typeOfJSONNumber = reflect.TypeOf(json.Number(""))
|
|
||||||
typeOfTime = reflect.TypeOf(time.Time{})
|
|
||||||
|
|
||||||
facebookSuccessJsonBytes = []byte("true")
|
|
||||||
)
|
|
||||||
1469
Godeps/_workspace/src/github.com/huandu/facebook/facebook_test.go
сгенерированный
поставляемый
1469
Godeps/_workspace/src/github.com/huandu/facebook/facebook_test.go
сгенерированный
поставляемый
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
131
Godeps/_workspace/src/github.com/huandu/facebook/misc.go
сгенерированный
поставляемый
131
Godeps/_workspace/src/github.com/huandu/facebook/misc.go
сгенерированный
поставляемый
@@ -1,131 +0,0 @@
|
|||||||
// A facebook graph api client in go.
|
|
||||||
// https://github.com/huandu/facebook/
|
|
||||||
//
|
|
||||||
// Copyright 2012 - 2015, Huan Du
|
|
||||||
// Licensed under the MIT license
|
|
||||||
// https://github.com/huandu/facebook/blob/master/LICENSE
|
|
||||||
|
|
||||||
package facebook
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
func camelCaseToUnderScore(str string) string {
|
|
||||||
if len(str) == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
var prev, r0, r1 rune
|
|
||||||
var size int
|
|
||||||
|
|
||||||
r0 = '_'
|
|
||||||
|
|
||||||
for len(str) > 0 {
|
|
||||||
prev = r0
|
|
||||||
r0, size = utf8.DecodeRuneInString(str)
|
|
||||||
str = str[size:]
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case r0 == utf8.RuneError:
|
|
||||||
buf.WriteByte(byte(str[0]))
|
|
||||||
|
|
||||||
case unicode.IsUpper(r0):
|
|
||||||
if prev != '_' {
|
|
||||||
buf.WriteRune('_')
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.WriteRune(unicode.ToLower(r0))
|
|
||||||
|
|
||||||
if len(str) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
r0, size = utf8.DecodeRuneInString(str)
|
|
||||||
str = str[size:]
|
|
||||||
|
|
||||||
if !unicode.IsUpper(r0) {
|
|
||||||
buf.WriteRune(r0)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// find next non-upper-case character and insert `_` properly.
|
|
||||||
// it's designed to convert `HTTPServer` to `http_server`.
|
|
||||||
// if there are more than 2 adjacent upper case characters in a word,
|
|
||||||
// treat them as an abbreviation plus a normal word.
|
|
||||||
for len(str) > 0 {
|
|
||||||
r1 = r0
|
|
||||||
r0, size = utf8.DecodeRuneInString(str)
|
|
||||||
str = str[size:]
|
|
||||||
|
|
||||||
if r0 == utf8.RuneError {
|
|
||||||
buf.WriteRune(unicode.ToLower(r1))
|
|
||||||
buf.WriteByte(byte(str[0]))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if !unicode.IsUpper(r0) {
|
|
||||||
if r0 == '_' || r0 == ' ' || r0 == '-' {
|
|
||||||
r0 = '_'
|
|
||||||
|
|
||||||
buf.WriteRune(unicode.ToLower(r1))
|
|
||||||
} else {
|
|
||||||
buf.WriteRune('_')
|
|
||||||
buf.WriteRune(unicode.ToLower(r1))
|
|
||||||
buf.WriteRune(r0)
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.WriteRune(unicode.ToLower(r1))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(str) == 0 || r0 == '_' {
|
|
||||||
buf.WriteRune(unicode.ToLower(r0))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
if r0 == ' ' || r0 == '-' {
|
|
||||||
r0 = '_'
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.WriteRune(r0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns error string.
|
|
||||||
func (e *Error) Error() string {
|
|
||||||
return e.Message
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new binary data holder.
|
|
||||||
func Data(filename string, source io.Reader) *binaryData {
|
|
||||||
return &binaryData{
|
|
||||||
Filename: filename,
|
|
||||||
Source: source,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a binary file holder.
|
|
||||||
func File(filename, path string) *binaryFile {
|
|
||||||
return &binaryFile{
|
|
||||||
Filename: filename,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a binary file holder and specific a different path for reading.
|
|
||||||
func FileAlias(filename, path string) *binaryFile {
|
|
||||||
return &binaryFile{
|
|
||||||
Filename: filename,
|
|
||||||
Path: path,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
146
Godeps/_workspace/src/github.com/huandu/facebook/paging_result.go
сгенерированный
поставляемый
146
Godeps/_workspace/src/github.com/huandu/facebook/paging_result.go
сгенерированный
поставляемый
@@ -1,146 +0,0 @@
|
|||||||
// A facebook graph api client in go.
|
|
||||||
// https://github.com/huandu/facebook/
|
|
||||||
//
|
|
||||||
// Copyright 2012 - 2015, Huan Du
|
|
||||||
// Licensed under the MIT license
|
|
||||||
// https://github.com/huandu/facebook/blob/master/LICENSE
|
|
||||||
|
|
||||||
package facebook
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type pagingData struct {
|
|
||||||
Data []Result `facebook:",required"`
|
|
||||||
Paging *pagingNavigator
|
|
||||||
}
|
|
||||||
|
|
||||||
type pagingNavigator struct {
|
|
||||||
Previous string
|
|
||||||
Next string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPagingResult(session *Session, res Result) (*PagingResult, error) {
|
|
||||||
// quick check whether Result is a paging response.
|
|
||||||
if _, ok := res["data"]; !ok {
|
|
||||||
return nil, fmt.Errorf("current Result is not a paging response.")
|
|
||||||
}
|
|
||||||
|
|
||||||
pr := &PagingResult{
|
|
||||||
session: session,
|
|
||||||
}
|
|
||||||
paging := &pr.paging
|
|
||||||
err := res.Decode(paging)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if paging.Paging != nil {
|
|
||||||
pr.previous = paging.Paging.Previous
|
|
||||||
pr.next = paging.Paging.Next
|
|
||||||
}
|
|
||||||
|
|
||||||
return pr, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get current data.
|
|
||||||
func (pr *PagingResult) Data() []Result {
|
|
||||||
return pr.paging.Data
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decodes the current full result to a struct. See Result#Decode.
|
|
||||||
func (pr *PagingResult) Decode(v interface{}) (err error) {
|
|
||||||
res := Result{
|
|
||||||
"data": pr.Data(),
|
|
||||||
}
|
|
||||||
return res.Decode(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read previous page.
|
|
||||||
func (pr *PagingResult) Previous() (noMore bool, err error) {
|
|
||||||
if !pr.HasPrevious() {
|
|
||||||
noMore = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return pr.navigate(&pr.previous)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read next page.
|
|
||||||
func (pr *PagingResult) Next() (noMore bool, err error) {
|
|
||||||
if !pr.HasNext() {
|
|
||||||
noMore = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return pr.navigate(&pr.next)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether there is previous page.
|
|
||||||
func (pr *PagingResult) HasPrevious() bool {
|
|
||||||
return pr.previous != ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether there is next page.
|
|
||||||
func (pr *PagingResult) HasNext() bool {
|
|
||||||
return pr.next != ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pr *PagingResult) navigate(url *string) (noMore bool, err error) {
|
|
||||||
var pagingUrl string
|
|
||||||
|
|
||||||
// add session information in paging url.
|
|
||||||
params := Params{}
|
|
||||||
pr.session.prepareParams(params)
|
|
||||||
|
|
||||||
if len(params) == 0 {
|
|
||||||
pagingUrl = *url
|
|
||||||
} else {
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
buf.WriteString(*url)
|
|
||||||
buf.WriteRune('&')
|
|
||||||
params.Encode(buf)
|
|
||||||
|
|
||||||
pagingUrl = buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
var request *http.Request
|
|
||||||
var res Result
|
|
||||||
|
|
||||||
request, err = http.NewRequest("GET", pagingUrl, nil)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err = pr.session.Request(request)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if pr.paging.Paging != nil {
|
|
||||||
pr.paging.Paging.Next = ""
|
|
||||||
pr.paging.Paging.Previous = ""
|
|
||||||
}
|
|
||||||
paging := &pr.paging
|
|
||||||
err = res.Decode(paging)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if paging.Paging == nil || len(paging.Data) == 0 {
|
|
||||||
*url = ""
|
|
||||||
noMore = true
|
|
||||||
} else {
|
|
||||||
pr.previous = paging.Paging.Previous
|
|
||||||
pr.next = paging.Paging.Next
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
227
Godeps/_workspace/src/github.com/huandu/facebook/params.go
сгенерированный
поставляемый
227
Godeps/_workspace/src/github.com/huandu/facebook/params.go
сгенерированный
поставляемый
@@ -1,227 +0,0 @@
|
|||||||
// A facebook graph api client in go.
|
|
||||||
// https://github.com/huandu/facebook/
|
|
||||||
//
|
|
||||||
// Copyright 2012 - 2015, Huan Du
|
|
||||||
// Licensed under the MIT license
|
|
||||||
// https://github.com/huandu/facebook/blob/master/LICENSE
|
|
||||||
|
|
||||||
package facebook
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"mime/multipart"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Makes a new Params instance by given data.
|
|
||||||
// Data must be a struct or a map with string keys.
|
|
||||||
// MakeParams will change all struct field name to lower case name with underscore.
|
|
||||||
// e.g. "FooBar" will be changed to "foo_bar".
|
|
||||||
//
|
|
||||||
// Returns nil if data cannot be used to make a Params instance.
|
|
||||||
func MakeParams(data interface{}) (params Params) {
|
|
||||||
if p, ok := data.(Params); ok {
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
if _, ok := r.(runtime.Error); ok {
|
|
||||||
panic(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
params = nil
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
params = makeParams(reflect.ValueOf(data))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeParams(value reflect.Value) (params Params) {
|
|
||||||
for value.Kind() == reflect.Ptr || value.Kind() == reflect.Interface {
|
|
||||||
value = value.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
// only map with string keys can be converted to Params
|
|
||||||
if value.Kind() == reflect.Map && value.Type().Key().Kind() == reflect.String {
|
|
||||||
params = Params{}
|
|
||||||
|
|
||||||
for _, key := range value.MapKeys() {
|
|
||||||
params[key.String()] = value.MapIndex(key).Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if value.Kind() != reflect.Struct {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
params = Params{}
|
|
||||||
num := value.NumField()
|
|
||||||
|
|
||||||
for i := 0; i < num; i++ {
|
|
||||||
name := camelCaseToUnderScore(value.Type().Field(i).Name)
|
|
||||||
field := value.Field(i)
|
|
||||||
|
|
||||||
for field.Kind() == reflect.Ptr {
|
|
||||||
field = field.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
switch field.Kind() {
|
|
||||||
case reflect.Chan, reflect.Func, reflect.UnsafePointer, reflect.Invalid:
|
|
||||||
// these types won't be marshalled in json.
|
|
||||||
params = nil
|
|
||||||
return
|
|
||||||
|
|
||||||
default:
|
|
||||||
params[name] = field.Interface()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encodes params to query string.
|
|
||||||
// If map value is not a string, Encode uses json.Marshal() to convert value to string.
|
|
||||||
//
|
|
||||||
// Encode will panic if Params contains values that cannot be marshalled to json string.
|
|
||||||
func (params Params) Encode(writer io.Writer) (mime string, err error) {
|
|
||||||
if params == nil || len(params) == 0 {
|
|
||||||
mime = _MIME_FORM_URLENCODED
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// check whether params contains any binary data.
|
|
||||||
hasBinary := false
|
|
||||||
|
|
||||||
for _, v := range params {
|
|
||||||
typ := reflect.TypeOf(v)
|
|
||||||
|
|
||||||
if typ == typeOfPointerToBinaryData || typ == typeOfPointerToBinaryFile {
|
|
||||||
hasBinary = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if hasBinary {
|
|
||||||
return params.encodeMultipartForm(writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
return params.encodeFormUrlEncoded(writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (params Params) encodeFormUrlEncoded(writer io.Writer) (mime string, err error) {
|
|
||||||
var jsonStr []byte
|
|
||||||
written := false
|
|
||||||
|
|
||||||
for k, v := range params {
|
|
||||||
if written {
|
|
||||||
io.WriteString(writer, "&")
|
|
||||||
}
|
|
||||||
|
|
||||||
io.WriteString(writer, url.QueryEscape(k))
|
|
||||||
io.WriteString(writer, "=")
|
|
||||||
|
|
||||||
if reflect.TypeOf(v).Kind() == reflect.String {
|
|
||||||
io.WriteString(writer, url.QueryEscape(reflect.ValueOf(v).String()))
|
|
||||||
} else {
|
|
||||||
jsonStr, err = json.Marshal(v)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
io.WriteString(writer, url.QueryEscape(string(jsonStr)))
|
|
||||||
}
|
|
||||||
|
|
||||||
written = true
|
|
||||||
}
|
|
||||||
|
|
||||||
mime = _MIME_FORM_URLENCODED
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (params Params) encodeMultipartForm(writer io.Writer) (mime string, err error) {
|
|
||||||
w := multipart.NewWriter(writer)
|
|
||||||
defer func() {
|
|
||||||
w.Close()
|
|
||||||
mime = w.FormDataContentType()
|
|
||||||
}()
|
|
||||||
|
|
||||||
for k, v := range params {
|
|
||||||
switch value := v.(type) {
|
|
||||||
case *binaryData:
|
|
||||||
var dst io.Writer
|
|
||||||
dst, err = w.CreateFormFile(k, value.Filename)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = io.Copy(dst, value.Source)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
case *binaryFile:
|
|
||||||
var dst io.Writer
|
|
||||||
var file *os.File
|
|
||||||
var path string
|
|
||||||
|
|
||||||
dst, err = w.CreateFormFile(k, value.Filename)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if value.Path == "" {
|
|
||||||
path = value.Filename
|
|
||||||
} else {
|
|
||||||
path = value.Path
|
|
||||||
}
|
|
||||||
|
|
||||||
file, err = os.Open(path)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = io.Copy(dst, file)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
var dst io.Writer
|
|
||||||
var jsonStr []byte
|
|
||||||
|
|
||||||
dst, err = w.CreateFormField(k)
|
|
||||||
|
|
||||||
if reflect.TypeOf(v).Kind() == reflect.String {
|
|
||||||
io.WriteString(dst, reflect.ValueOf(v).String())
|
|
||||||
} else {
|
|
||||||
jsonStr, err = json.Marshal(v)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = dst.Write(jsonStr)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
1097
Godeps/_workspace/src/github.com/huandu/facebook/result.go
сгенерированный
поставляемый
1097
Godeps/_workspace/src/github.com/huandu/facebook/result.go
сгенерированный
поставляемый
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
667
Godeps/_workspace/src/github.com/huandu/facebook/session.go
сгенерированный
поставляемый
667
Godeps/_workspace/src/github.com/huandu/facebook/session.go
сгенерированный
поставляемый
@@ -1,667 +0,0 @@
|
|||||||
// A facebook graph api client in go.
|
|
||||||
// https://github.com/huandu/facebook/
|
|
||||||
//
|
|
||||||
// Copyright 2012 - 2015, Huan Du
|
|
||||||
// Licensed under the MIT license
|
|
||||||
// https://github.com/huandu/facebook/blob/master/LICENSE
|
|
||||||
|
|
||||||
package facebook
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Makes a facebook graph api call.
|
|
||||||
//
|
|
||||||
// If session access token is set, "access_token" in params will be set to the token value.
|
|
||||||
//
|
|
||||||
// Returns facebook graph api call result.
|
|
||||||
// If facebook returns error in response, returns error details in res and set err.
|
|
||||||
func (session *Session) Api(path string, method Method, params Params) (Result, error) {
|
|
||||||
return session.graph(path, method, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get is a short hand of Api(path, GET, params).
|
|
||||||
func (session *Session) Get(path string, params Params) (Result, error) {
|
|
||||||
return session.Api(path, GET, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post is a short hand of Api(path, POST, params).
|
|
||||||
func (session *Session) Post(path string, params Params) (Result, error) {
|
|
||||||
return session.Api(path, POST, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete is a short hand of Api(path, DELETE, params).
|
|
||||||
func (session *Session) Delete(path string, params Params) (Result, error) {
|
|
||||||
return session.Api(path, DELETE, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put is a short hand of Api(path, PUT, params).
|
|
||||||
func (session *Session) Put(path string, params Params) (Result, error) {
|
|
||||||
return session.Api(path, PUT, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Makes a batch call. Each params represent a single facebook graph api call.
|
|
||||||
//
|
|
||||||
// BatchApi supports most kinds of batch calls defines in facebook batch api document,
|
|
||||||
// except uploading binary data. Use Batch to upload binary data.
|
|
||||||
//
|
|
||||||
// If session access token is set, the token will be used in batch api call.
|
|
||||||
//
|
|
||||||
// Returns an array of batch call result on success.
|
|
||||||
//
|
|
||||||
// Facebook document: https://developers.facebook.com/docs/graph-api/making-multiple-requests
|
|
||||||
func (session *Session) BatchApi(params ...Params) ([]Result, error) {
|
|
||||||
return session.Batch(nil, params...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Makes a batch facebook graph api call.
|
|
||||||
// Batch is designed for more advanced usage including uploading binary files.
|
|
||||||
//
|
|
||||||
// If session access token is set, "access_token" in batchParams will be set to the token value.
|
|
||||||
//
|
|
||||||
// Facebook document: https://developers.facebook.com/docs/graph-api/making-multiple-requests
|
|
||||||
func (session *Session) Batch(batchParams Params, params ...Params) ([]Result, error) {
|
|
||||||
return session.graphBatch(batchParams, params...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Makes a FQL query.
|
|
||||||
// Returns a slice of Result. If there is no query result, the result is nil.
|
|
||||||
//
|
|
||||||
// Facebook document: https://developers.facebook.com/docs/technical-guides/fql#query
|
|
||||||
func (session *Session) FQL(query string) ([]Result, error) {
|
|
||||||
res, err := session.graphFQL(Params{
|
|
||||||
"q": query,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// query result is stored in "data" field.
|
|
||||||
var data []Result
|
|
||||||
err = res.DecodeField("data", &data)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Makes a multi FQL query.
|
|
||||||
// Returns a parsed Result. The key is the multi query key, and the value is the query result.
|
|
||||||
//
|
|
||||||
// Here is a multi-query sample.
|
|
||||||
//
|
|
||||||
// res, _ := session.MultiFQL(Params{
|
|
||||||
// "query1": "SELECT name FROM user WHERE uid = me()",
|
|
||||||
// "query2": "SELECT uid1, uid2 FROM friend WHERE uid1 = me()",
|
|
||||||
// })
|
|
||||||
//
|
|
||||||
// // Get query results from response.
|
|
||||||
// var query1, query2 []Result
|
|
||||||
// res.DecodeField("query1", &query1)
|
|
||||||
// res.DecodeField("query2", &query2)
|
|
||||||
//
|
|
||||||
// Facebook document: https://developers.facebook.com/docs/technical-guides/fql#multi
|
|
||||||
func (session *Session) MultiFQL(queries Params) (Result, error) {
|
|
||||||
res, err := session.graphFQL(Params{
|
|
||||||
"q": queries,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// query result is stored in "data" field.
|
|
||||||
var data []Result
|
|
||||||
err = res.DecodeField("data", &data)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if data == nil {
|
|
||||||
return nil, fmt.Errorf("multi-fql result is not found.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Multi-fql data structure is:
|
|
||||||
// {
|
|
||||||
// "data": [
|
|
||||||
// {
|
|
||||||
// "name": "query1",
|
|
||||||
// "fql_result_set": [
|
|
||||||
// {...}, {...}, ...
|
|
||||||
// ]
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// "name": "query2",
|
|
||||||
// "fql_result_set": [
|
|
||||||
// {...}, {...}, ...
|
|
||||||
// ]
|
|
||||||
// },
|
|
||||||
// ...
|
|
||||||
// ]
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Parse the structure to following go map.
|
|
||||||
// {
|
|
||||||
// "query1": [
|
|
||||||
// // Come from field "fql_result_set".
|
|
||||||
// {...}, {...}, ...
|
|
||||||
// ],
|
|
||||||
// "query2": [
|
|
||||||
// {...}, {...}, ...
|
|
||||||
// ],
|
|
||||||
// ...
|
|
||||||
// }
|
|
||||||
var name string
|
|
||||||
var apiResponse interface{}
|
|
||||||
var ok bool
|
|
||||||
result := Result{}
|
|
||||||
|
|
||||||
for k, v := range data {
|
|
||||||
err = v.DecodeField("name", &name)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("missing required field 'name' in multi-query data.%v. %v", k, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
apiResponse, ok = v["fql_result_set"]
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("missing required field 'fql_result_set' in multi-query data.%v.", k)
|
|
||||||
}
|
|
||||||
|
|
||||||
result[name] = apiResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Makes an arbitrary HTTP request.
|
|
||||||
// It expects server responses a facebook Graph API response.
|
|
||||||
// request, _ := http.NewRequest("https://graph.facebook.com/538744468", "GET", nil)
|
|
||||||
// res, err := session.Request(request)
|
|
||||||
// fmt.Println(res["gender"]) // get "male"
|
|
||||||
func (session *Session) Request(request *http.Request) (res Result, err error) {
|
|
||||||
var response *http.Response
|
|
||||||
var data []byte
|
|
||||||
|
|
||||||
response, data, err = session.sendRequest(request)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err = MakeResult(data)
|
|
||||||
session.addDebugInfo(res, response)
|
|
||||||
|
|
||||||
if res != nil {
|
|
||||||
err = res.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets current user id from access token.
|
|
||||||
//
|
|
||||||
// Returns error if access token is not set or invalid.
|
|
||||||
//
|
|
||||||
// It's a standard way to validate a facebook access token.
|
|
||||||
func (session *Session) User() (id string, err error) {
|
|
||||||
if session.id != "" {
|
|
||||||
id = session.id
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if session.accessToken == "" {
|
|
||||||
err = fmt.Errorf("access token is not set.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var result Result
|
|
||||||
result, err = session.Api("/me", GET, Params{"fields": "id"})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = result.DecodeField("id", &id)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validates Session access token.
|
|
||||||
// Returns nil if access token is valid.
|
|
||||||
func (session *Session) Validate() (err error) {
|
|
||||||
if session.accessToken == "" {
|
|
||||||
err = fmt.Errorf("access token is not set.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var result Result
|
|
||||||
result, err = session.Api("/me", GET, Params{"fields": "id"})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if f := result.Get("id"); f == nil {
|
|
||||||
err = fmt.Errorf("invalid access token.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inspect Session access token.
|
|
||||||
// Returns JSON array containing data about the inspected token.
|
|
||||||
// See https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.2#checktoken
|
|
||||||
func (session *Session) Inspect() (result Result, err error) {
|
|
||||||
if session.accessToken == "" {
|
|
||||||
err = fmt.Errorf("access token is not set.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if session.app == nil {
|
|
||||||
err = fmt.Errorf("cannot inspect access token without binding an app.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
appAccessToken := session.app.AppAccessToken()
|
|
||||||
|
|
||||||
if appAccessToken == "" {
|
|
||||||
err = fmt.Errorf("app access token is not set.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err = session.Api("/debug_token", GET, Params{
|
|
||||||
"input_token": session.accessToken,
|
|
||||||
"access_token": appAccessToken,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// facebook stores everything, including error, inside result["data"].
|
|
||||||
// make sure that result["data"] exists and doesn't contain error.
|
|
||||||
if _, ok := result["data"]; !ok {
|
|
||||||
err = fmt.Errorf("facebook inspect api returns unexpected result.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var data Result
|
|
||||||
result.DecodeField("data", &data)
|
|
||||||
result = data
|
|
||||||
err = result.Err()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets current access token.
|
|
||||||
func (session *Session) AccessToken() string {
|
|
||||||
return session.accessToken
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets a new access token.
|
|
||||||
func (session *Session) SetAccessToken(token string) {
|
|
||||||
if token != session.accessToken {
|
|
||||||
session.id = ""
|
|
||||||
session.accessToken = token
|
|
||||||
session.appsecretProof = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check appsecret proof is enabled or not.
|
|
||||||
func (session *Session) AppsecretProof() string {
|
|
||||||
if !session.enableAppsecretProof {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if session.accessToken == "" || session.app == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if session.appsecretProof == "" {
|
|
||||||
hash := hmac.New(sha256.New, []byte(session.app.AppSecret))
|
|
||||||
hash.Write([]byte(session.accessToken))
|
|
||||||
session.appsecretProof = hex.EncodeToString(hash.Sum(nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
return session.appsecretProof
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable or disable appsecret proof status.
|
|
||||||
// Returns error if there is no App associasted with this Session.
|
|
||||||
func (session *Session) EnableAppsecretProof(enabled bool) error {
|
|
||||||
if session.app == nil {
|
|
||||||
return fmt.Errorf("cannot change appsecret proof status without an associated App.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if session.enableAppsecretProof != enabled {
|
|
||||||
session.enableAppsecretProof = enabled
|
|
||||||
|
|
||||||
// reset pre-calculated proof here to give caller a way to do so in some rare case,
|
|
||||||
// e.g. associated app's secret is changed.
|
|
||||||
session.appsecretProof = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets associated App.
|
|
||||||
func (session *Session) App() *App {
|
|
||||||
return session.app
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug returns current debug mode.
|
|
||||||
func (session *Session) Debug() DebugMode {
|
|
||||||
if session.debug != DEBUG_OFF {
|
|
||||||
return session.debug
|
|
||||||
}
|
|
||||||
|
|
||||||
return Debug
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetDebug updates per session debug mode and returns old mode.
|
|
||||||
// If per session debug mode is DEBUG_OFF, session will use global
|
|
||||||
// Debug mode.
|
|
||||||
func (session *Session) SetDebug(debug DebugMode) DebugMode {
|
|
||||||
old := session.debug
|
|
||||||
session.debug = debug
|
|
||||||
return old
|
|
||||||
}
|
|
||||||
|
|
||||||
func (session *Session) graph(path string, method Method, params Params) (res Result, err error) {
|
|
||||||
var graphUrl string
|
|
||||||
|
|
||||||
if params == nil {
|
|
||||||
params = Params{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// always format as json.
|
|
||||||
params["format"] = "json"
|
|
||||||
|
|
||||||
// overwrite method as we always use post
|
|
||||||
params["method"] = method
|
|
||||||
|
|
||||||
// get graph api url.
|
|
||||||
if session.isVideoPost(path, method) {
|
|
||||||
graphUrl = session.getUrl("graph_video", path, nil)
|
|
||||||
} else {
|
|
||||||
graphUrl = session.getUrl("graph", path, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
var response *http.Response
|
|
||||||
response, err = session.sendPostRequest(graphUrl, params, &res)
|
|
||||||
session.addDebugInfo(res, response)
|
|
||||||
|
|
||||||
if res != nil {
|
|
||||||
err = res.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (session *Session) graphBatch(batchParams Params, params ...Params) ([]Result, error) {
|
|
||||||
if batchParams == nil {
|
|
||||||
batchParams = Params{}
|
|
||||||
}
|
|
||||||
|
|
||||||
batchParams["batch"] = params
|
|
||||||
|
|
||||||
var res []Result
|
|
||||||
graphUrl := session.getUrl("graph", "", nil)
|
|
||||||
_, err := session.sendPostRequest(graphUrl, batchParams, &res)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (session *Session) graphFQL(params Params) (res Result, err error) {
|
|
||||||
if params == nil {
|
|
||||||
params = Params{}
|
|
||||||
}
|
|
||||||
|
|
||||||
session.prepareParams(params)
|
|
||||||
|
|
||||||
// encode url.
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
buf.WriteString(domainMap["graph"])
|
|
||||||
buf.WriteString("fql?")
|
|
||||||
_, err = params.Encode(buf)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot encode params. %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// it seems facebook disallow POST to /fql. always use GET for FQL.
|
|
||||||
var response *http.Response
|
|
||||||
response, err = session.sendGetRequest(buf.String(), &res)
|
|
||||||
session.addDebugInfo(res, response)
|
|
||||||
|
|
||||||
if res != nil {
|
|
||||||
err = res.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (session *Session) prepareParams(params Params) {
|
|
||||||
if _, ok := params["access_token"]; !ok && session.accessToken != "" {
|
|
||||||
params["access_token"] = session.accessToken
|
|
||||||
}
|
|
||||||
|
|
||||||
if session.enableAppsecretProof && session.accessToken != "" && session.app != nil {
|
|
||||||
params["appsecret_proof"] = session.AppsecretProof()
|
|
||||||
}
|
|
||||||
|
|
||||||
debug := session.Debug()
|
|
||||||
|
|
||||||
if debug != DEBUG_OFF {
|
|
||||||
params["debug"] = debug
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (session *Session) sendGetRequest(uri string, res interface{}) (*http.Response, error) {
|
|
||||||
request, err := http.NewRequest("GET", uri, nil)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
response, data, err := session.sendRequest(request)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = makeResult(data, res)
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (session *Session) sendPostRequest(uri string, params Params, res interface{}) (*http.Response, error) {
|
|
||||||
session.prepareParams(params)
|
|
||||||
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
mime, err := params.Encode(buf)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot encode POST params. %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var request *http.Request
|
|
||||||
|
|
||||||
request, err = http.NewRequest("POST", uri, buf)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
request.Header.Set("Content-Type", mime)
|
|
||||||
response, data, err := session.sendRequest(request)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = makeResult(data, res)
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (session *Session) sendOauthRequest(uri string, params Params) (Result, error) {
|
|
||||||
urlStr := session.getUrl("graph", uri, nil)
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
mime, err := params.Encode(buf)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot encode POST params. %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var request *http.Request
|
|
||||||
|
|
||||||
request, err = http.NewRequest("POST", urlStr, buf)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
request.Header.Set("Content-Type", mime)
|
|
||||||
_, data, err := session.sendRequest(request)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(data) == 0 {
|
|
||||||
return nil, fmt.Errorf("empty response from facebook")
|
|
||||||
}
|
|
||||||
|
|
||||||
// facebook may return a query string.
|
|
||||||
if 'a' <= data[0] && data[0] <= 'z' {
|
|
||||||
query, err := url.ParseQuery(string(data))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert a query to Result.
|
|
||||||
res := Result{}
|
|
||||||
|
|
||||||
for k := range query {
|
|
||||||
res[k] = query.Get(k)
|
|
||||||
}
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := MakeResult(data)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (session *Session) sendRequest(request *http.Request) (response *http.Response, data []byte, err error) {
|
|
||||||
if session.HttpClient == nil {
|
|
||||||
response, err = http.DefaultClient.Do(request)
|
|
||||||
} else {
|
|
||||||
response, err = session.HttpClient.Do(request)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("cannot reach facebook server. %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
_, err = io.Copy(buf, response.Body)
|
|
||||||
response.Body.Close()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("cannot read facebook response. %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
data = buf.Bytes()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (session *Session) isVideoPost(path string, method Method) bool {
|
|
||||||
return method == POST && regexpIsVideoPost.MatchString(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (session *Session) getUrl(name, path string, params Params) string {
|
|
||||||
offset := 0
|
|
||||||
|
|
||||||
if path != "" && path[0] == '/' {
|
|
||||||
offset = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
buf.WriteString(domainMap[name])
|
|
||||||
|
|
||||||
// facebook versioning.
|
|
||||||
if session.Version == "" {
|
|
||||||
if Version != "" {
|
|
||||||
buf.WriteString(Version)
|
|
||||||
buf.WriteRune('/')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
buf.WriteString(session.Version)
|
|
||||||
buf.WriteRune('/')
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.WriteString(string(path[offset:]))
|
|
||||||
|
|
||||||
if params != nil {
|
|
||||||
buf.WriteRune('?')
|
|
||||||
params.Encode(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (session *Session) addDebugInfo(res Result, response *http.Response) Result {
|
|
||||||
if session.Debug() == DEBUG_OFF || res == nil || response == nil {
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
debugInfo := make(map[string]interface{})
|
|
||||||
|
|
||||||
// save debug information in result directly.
|
|
||||||
res.DecodeField("__debug__", &debugInfo)
|
|
||||||
debugInfo[debugProtoKey] = response.Proto
|
|
||||||
debugInfo[debugHeaderKey] = response.Header
|
|
||||||
|
|
||||||
res["__debug__"] = debugInfo
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeBase64URLEncodingString(data string) ([]byte, error) {
|
|
||||||
buf := bytes.NewBufferString(data)
|
|
||||||
|
|
||||||
// go's URLEncoding implementation requires base64 padding.
|
|
||||||
if m := len(data) % 4; m != 0 {
|
|
||||||
buf.WriteString(strings.Repeat("=", 4-m))
|
|
||||||
}
|
|
||||||
|
|
||||||
reader := base64.NewDecoder(base64.URLEncoding, buf)
|
|
||||||
output := &bytes.Buffer{}
|
|
||||||
_, err := io.Copy(output, reader)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return output.Bytes(), nil
|
|
||||||
}
|
|
||||||
127
Godeps/_workspace/src/github.com/huandu/facebook/type.go
сгенерированный
поставляемый
127
Godeps/_workspace/src/github.com/huandu/facebook/type.go
сгенерированный
поставляемый
@@ -1,127 +0,0 @@
|
|||||||
// A facebook graph api client in go.
|
|
||||||
// https://github.com/huandu/facebook/
|
|
||||||
//
|
|
||||||
// Copyright 2012 - 2015, Huan Du
|
|
||||||
// Licensed under the MIT license
|
|
||||||
// https://github.com/huandu/facebook/blob/master/LICENSE
|
|
||||||
|
|
||||||
package facebook
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Holds facebook application information.
|
|
||||||
type App struct {
|
|
||||||
// Facebook app id
|
|
||||||
AppId string
|
|
||||||
|
|
||||||
// Facebook app secret
|
|
||||||
AppSecret string
|
|
||||||
|
|
||||||
// Facebook app redirect URI in the app's configuration.
|
|
||||||
RedirectUri string
|
|
||||||
|
|
||||||
// Enable appsecret proof in every API call to facebook.
|
|
||||||
// Facebook document: https://developers.facebook.com/docs/graph-api/securing-requests
|
|
||||||
EnableAppsecretProof bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// An interface to send http request.
|
|
||||||
// This interface is designed to be compatible with type `*http.Client`.
|
|
||||||
type HttpClient interface {
|
|
||||||
Do(req *http.Request) (resp *http.Response, err error)
|
|
||||||
Get(url string) (resp *http.Response, err error)
|
|
||||||
Post(url string, bodyType string, body io.Reader) (resp *http.Response, err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Holds a facebook session with an access token.
|
|
||||||
// Session should be created by App.Session or App.SessionFromSignedRequest.
|
|
||||||
type Session struct {
|
|
||||||
HttpClient HttpClient
|
|
||||||
Version string // facebook versioning.
|
|
||||||
|
|
||||||
accessToken string // facebook access token. can be empty.
|
|
||||||
app *App
|
|
||||||
id string
|
|
||||||
|
|
||||||
enableAppsecretProof bool // add "appsecret_proof" parameter in every facebook API call.
|
|
||||||
appsecretProof string // pre-calculated "appsecret_proof" value.
|
|
||||||
|
|
||||||
debug DebugMode // using facebook debugging api in every request.
|
|
||||||
}
|
|
||||||
|
|
||||||
// API HTTP method.
|
|
||||||
// Can be GET, POST or DELETE.
|
|
||||||
type Method string
|
|
||||||
|
|
||||||
// Graph API debug mode.
|
|
||||||
// See https://developers.facebook.com/docs/graph-api/using-graph-api/v2.3#graphapidebugmode
|
|
||||||
type DebugMode string
|
|
||||||
|
|
||||||
// API params.
|
|
||||||
//
|
|
||||||
// For general uses, just use Params as a ordinary map.
|
|
||||||
//
|
|
||||||
// For advanced uses, use MakeParams to create Params from any struct.
|
|
||||||
type Params map[string]interface{}
|
|
||||||
|
|
||||||
// Facebook API call result.
|
|
||||||
type Result map[string]interface{}
|
|
||||||
|
|
||||||
// Represents facebook API call result with paging information.
|
|
||||||
type PagingResult struct {
|
|
||||||
session *Session
|
|
||||||
paging pagingData
|
|
||||||
previous string
|
|
||||||
next string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Represents facebook batch API call result.
|
|
||||||
// See https://developers.facebook.com/docs/graph-api/making-multiple-requests/#multiple_methods.
|
|
||||||
type BatchResult struct {
|
|
||||||
StatusCode int // HTTP status code.
|
|
||||||
Header http.Header // HTTP response headers.
|
|
||||||
Body string // Raw HTTP response body string.
|
|
||||||
Result Result // Facebook api result parsed from body.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Facebook API error.
|
|
||||||
type Error struct {
|
|
||||||
Message string
|
|
||||||
Type string
|
|
||||||
Code int
|
|
||||||
ErrorSubcode int // subcode for authentication related errors.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Binary data.
|
|
||||||
type binaryData struct {
|
|
||||||
Filename string // filename used in multipart form writer.
|
|
||||||
Source io.Reader // file data source.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Binary file.
|
|
||||||
type binaryFile struct {
|
|
||||||
Filename string // filename used in multipart form writer.
|
|
||||||
Path string // path to file. must be readable.
|
|
||||||
}
|
|
||||||
|
|
||||||
// DebugInfo is the debug information returned by facebook when debug mode is enabled.
|
|
||||||
type DebugInfo struct {
|
|
||||||
Messages []DebugMessage // debug messages. it can be nil if there is no message.
|
|
||||||
Header http.Header // all HTTP headers for this response.
|
|
||||||
Proto string // HTTP protocol name for this response.
|
|
||||||
|
|
||||||
// Facebook debug HTTP headers.
|
|
||||||
FacebookApiVersion string // the actual graph API version provided by facebook-api-version HTTP header.
|
|
||||||
FacebookDebug string // the X-FB-Debug HTTP header.
|
|
||||||
FacebookRev string // the x-fb-rev HTTP header.
|
|
||||||
}
|
|
||||||
|
|
||||||
// DebugMessage is one debug message in "__debug__" of graph API response.
|
|
||||||
type DebugMessage struct {
|
|
||||||
Type string
|
|
||||||
Message string
|
|
||||||
Link string
|
|
||||||
}
|
|
||||||
8
Makefile
8
Makefile
@@ -1,4 +1,4 @@
|
|||||||
.PHONY: all test clean build install run stop cover dist dist-battlehouse cleandb
|
.PHONY: all test clean build install run stop cover dist cleandb travis
|
||||||
|
|
||||||
GOPATH ?= $(GOPATH:)
|
GOPATH ?= $(GOPATH:)
|
||||||
GOFLAGS ?= $(GOFLAGS:)
|
GOFLAGS ?= $(GOFLAGS:)
|
||||||
@@ -15,7 +15,11 @@ DIST_RESULTS=$(DIST_ROOT)/results
|
|||||||
BENCH=.
|
BENCH=.
|
||||||
TESTS=.
|
TESTS=.
|
||||||
|
|
||||||
all: build
|
all: travis
|
||||||
|
|
||||||
|
travis:
|
||||||
|
@echo building for travis
|
||||||
|
@go build $(GOFLAGS) ./...
|
||||||
|
|
||||||
build:
|
build:
|
||||||
@go build $(GOFLAGS) ./...
|
@go build $(GOFLAGS) ./...
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user