mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
updating make
This commit is contained in:
13
Godeps/Godeps.json
generated
13
Godeps/Godeps.json
generated
@@ -88,11 +88,6 @@
|
||||
"ImportPath": "github.com/gorilla/websocket",
|
||||
"Rev": "6fd0f867fef40c540fa05c59f86396de10a632a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/huandu/facebook",
|
||||
"Comment": "v1.5.2",
|
||||
"Rev": "7c46d89211b9a3f01087f47f6a399b815801ade4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mssola/user_agent",
|
||||
"Comment": "v0.4.1-2-g35c7f18",
|
||||
@@ -122,6 +117,10 @@
|
||||
"ImportPath": "golang.org/x/crypto/blowfish",
|
||||
"Rev": "74f810a0152f4c50a16195f6b9ff44afc35594e8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/image/bmp",
|
||||
"Rev": "eb11b45157c1b71f30b3cec66306f1cd779a689e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/bufio.v1",
|
||||
"Comment": "v1",
|
||||
@@ -136,10 +135,6 @@
|
||||
"ImportPath": "gopkg.in/redis.v2",
|
||||
"Comment": "v2.3.2",
|
||||
"Rev": "e6179049628164864e6e84e973cfb56335748dea"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/image/bmp",
|
||||
"Rev": "eb11b45157c1b71f30b3cec66306f1cd779a689e"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
47
Godeps/_workspace/src/github.com/huandu/facebook/CHANGELOG.md
generated
vendored
47
Godeps/_workspace/src/github.com/huandu/facebook/CHANGELOG.md
generated
vendored
@@ -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
generated
vendored
7
Godeps/_workspace/src/github.com/huandu/facebook/CONTRIBUTING.md
generated
vendored
@@ -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
generated
vendored
19
Godeps/_workspace/src/github.com/huandu/facebook/LICENSE
generated
vendored
@@ -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
generated
vendored
347
Godeps/_workspace/src/github.com/huandu/facebook/README.md
generated
vendored
@@ -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
generated
vendored
180
Godeps/_workspace/src/github.com/huandu/facebook/api.go
generated
vendored
@@ -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
generated
vendored
255
Godeps/_workspace/src/github.com/huandu/facebook/app.go
generated
vendored
@@ -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
generated
vendored
52
Godeps/_workspace/src/github.com/huandu/facebook/batch_result.go
generated
vendored
@@ -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
generated
vendored
74
Godeps/_workspace/src/github.com/huandu/facebook/const.go
generated
vendored
@@ -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
generated
vendored
1469
Godeps/_workspace/src/github.com/huandu/facebook/facebook_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
131
Godeps/_workspace/src/github.com/huandu/facebook/misc.go
generated
vendored
131
Godeps/_workspace/src/github.com/huandu/facebook/misc.go
generated
vendored
@@ -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
generated
vendored
146
Godeps/_workspace/src/github.com/huandu/facebook/paging_result.go
generated
vendored
@@ -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
generated
vendored
227
Godeps/_workspace/src/github.com/huandu/facebook/params.go
generated
vendored
@@ -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
generated
vendored
1097
Godeps/_workspace/src/github.com/huandu/facebook/result.go
generated
vendored
File diff suppressed because it is too large
Load Diff
667
Godeps/_workspace/src/github.com/huandu/facebook/session.go
generated
vendored
667
Godeps/_workspace/src/github.com/huandu/facebook/session.go
generated
vendored
@@ -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
generated
vendored
127
Godeps/_workspace/src/github.com/huandu/facebook/type.go
generated
vendored
@@ -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:)
|
||||
GOFLAGS ?= $(GOFLAGS:)
|
||||
@@ -15,7 +15,11 @@ DIST_RESULTS=$(DIST_ROOT)/results
|
||||
BENCH=.
|
||||
TESTS=.
|
||||
|
||||
all: build
|
||||
all: travis
|
||||
|
||||
travis:
|
||||
@echo building for travis
|
||||
@go build $(GOFLAGS) ./...
|
||||
|
||||
build:
|
||||
@go build $(GOFLAGS) ./...
|
||||
|
||||
Reference in New Issue
Block a user