-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(integration): add Pocket support
- Add account integration package - Add account linking API - Add Pocket account linking - Add Pocket outgoing webhook
- Loading branch information
Showing
25 changed files
with
556 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package api | ||
|
||
import ( | ||
"errors" | ||
"net/http" | ||
"path" | ||
"strings" | ||
|
||
"github.com/ncarlier/readflow/pkg/config" | ||
"github.com/ncarlier/readflow/pkg/integration/account" | ||
|
||
// import all account providers | ||
_ "github.com/ncarlier/readflow/pkg/integration/account/all" | ||
) | ||
|
||
// linking is the handler used for account linking. | ||
func linking(conf *config.Config) http.Handler { | ||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
stub := strings.TrimPrefix(r.URL.Path, "/linking/") | ||
if stub == "" { | ||
http.Error(w, "not found", http.StatusNotFound) | ||
return | ||
} | ||
|
||
providerName := path.Dir(stub) | ||
provider, err := account.NewAccountProvider(providerName, conf) | ||
if err != nil { | ||
http.Error(w, err.Error(), http.StatusNotFound) | ||
return | ||
} | ||
|
||
action := path.Base(stub) | ||
if action == "request" { | ||
err = provider.RequestHandler(w, r) | ||
} else if action == "authorize" { | ||
err = provider.AuthorizeHandler(w, r) | ||
} else { | ||
err = errors.New("action non supported") | ||
} | ||
if err != nil { | ||
http.Error(w, err.Error(), http.StatusInternalServerError) | ||
return | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package account | ||
|
||
import ( | ||
"net/http" | ||
) | ||
|
||
// Provider used for account linking | ||
type Provider interface { | ||
RequestHandler(w http.ResponseWriter, r *http.Request) error | ||
AuthorizeHandler(w http.ResponseWriter, r *http.Request) error | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package all | ||
|
||
import ( | ||
// activate pocket account provider | ||
_ "github.com/ncarlier/readflow/pkg/integration/account/pocket" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package pocket | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"net/http" | ||
"net/url" | ||
"strings" | ||
|
||
"github.com/ncarlier/readflow/pkg/config" | ||
"github.com/ncarlier/readflow/pkg/constant" | ||
"github.com/ncarlier/readflow/pkg/integration/account" | ||
) | ||
|
||
type pocketProvider struct { | ||
key string | ||
} | ||
|
||
type pocketAuthorizeRequest struct { | ||
Key string `json:"consumer_key"` | ||
Code string `json:"code"` | ||
} | ||
|
||
type pocketRequestResponse struct { | ||
Redirect string `json:"redirect"` | ||
Code string `json:"code"` | ||
} | ||
|
||
func newPocketProvider(conf *config.Config) (account.Provider, error) { | ||
if conf.PocketConsumerKey == "" { | ||
return nil, errors.New("Pocket consumer key not set") | ||
} | ||
provider := &pocketProvider{ | ||
key: conf.PocketConsumerKey, | ||
} | ||
return provider, nil | ||
} | ||
|
||
// RequestHandler used for linking account request | ||
func (p *pocketProvider) RequestHandler(w http.ResponseWriter, r *http.Request) error { | ||
redirect, ok := r.URL.Query()["redirect_uri"] | ||
if !ok || len(redirect[0]) < 1 { | ||
return errors.New("missing redirect_uri parameter") | ||
} | ||
params := url.Values{} | ||
params.Set("consumer_key", p.key) | ||
params.Set("redirect_uri", redirect[0]) | ||
payload := strings.NewReader(params.Encode()) | ||
resp, err := http.Post("https://getpocket.com/v3/oauth/request", constant.ContentTypeForm, payload) | ||
if err != nil { | ||
return err | ||
} | ||
defer resp.Body.Close() | ||
|
||
if resp.StatusCode >= 300 { | ||
return fmt.Errorf("bad status code: %d", resp.StatusCode) | ||
} | ||
body, err := ioutil.ReadAll(resp.Body) | ||
if err != nil { | ||
return err | ||
} | ||
values, err := url.ParseQuery(string(body)) | ||
if err != nil { | ||
return err | ||
} | ||
code := values.Get("code") | ||
if code == "" { | ||
return errors.New("code is empty") | ||
} | ||
params.Del("consumer_key") | ||
params.Set("request_token", code) | ||
data := pocketRequestResponse{ | ||
Redirect: "https://getpocket.com/auth/authorize?" + params.Encode(), | ||
Code: code, | ||
} | ||
w.Header().Set("Content-Type", "application/json") | ||
w.WriteHeader(http.StatusAccepted) | ||
json.NewEncoder(w).Encode(data) | ||
return nil | ||
} | ||
|
||
// AuthorizeHandler used for linking account authorization request | ||
func (p *pocketProvider) AuthorizeHandler(w http.ResponseWriter, r *http.Request) error { | ||
code, ok := r.URL.Query()["code"] | ||
if !ok || len(code[0]) < 1 { | ||
return errors.New("missing code parameter") | ||
} | ||
params := pocketAuthorizeRequest{ | ||
Key: p.key, | ||
Code: code[0], | ||
} | ||
b := new(bytes.Buffer) | ||
json.NewEncoder(b).Encode(params) | ||
|
||
req, err := http.NewRequest("POST", "https://getpocket.com/v3/oauth/authorize", b) | ||
if err != nil { | ||
return err | ||
} | ||
req.Header.Set("Content-Type", constant.ContentTypeJSON) | ||
req.Header.Set("X-Accept", "application/json") | ||
client := &http.Client{} | ||
resp, err := client.Do(req) | ||
if err != nil { | ||
return err | ||
} | ||
defer resp.Body.Close() | ||
|
||
// Forward response | ||
w.Header().Set("Content-Type", "application/json") | ||
w.WriteHeader(resp.StatusCode) | ||
io.Copy(w, resp.Body) | ||
|
||
return nil | ||
} | ||
|
||
func init() { | ||
account.Register("pocket", &account.Def{ | ||
Name: "Pocket", | ||
Desc: "Put knowledge in your Pocket.", | ||
Create: newPocketProvider, | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package account | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/ncarlier/readflow/pkg/config" | ||
) | ||
|
||
// Creator function for create an account provider | ||
type Creator func(conf *config.Config) (Provider, error) | ||
|
||
// Def is a webhook provider definition | ||
type Def struct { | ||
Name string | ||
Desc string | ||
Create Creator | ||
} | ||
|
||
// Registry of all account provider | ||
var Registry = map[string]*Def{} | ||
|
||
// Register add account provider definition to the registry | ||
func Register(name string, def *Def) { | ||
Registry[name] = def | ||
} | ||
|
||
// NewAccountProvider create new account provider | ||
func NewAccountProvider(name string, conf *config.Config) (Provider, error) { | ||
def, ok := Registry[name] | ||
if !ok { | ||
return nil, fmt.Errorf("unknown account provider: %s", name) | ||
} | ||
return def.Create(conf) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.