From 7d0806382ead7cda9aced83e7448b6a826803ad5 Mon Sep 17 00:00:00 2001 From: Mark Hudnall Date: Mon, 15 Jun 2020 21:05:44 -0700 Subject: [PATCH] Clean up linking - Environment and public key were hardcoded - Static link template wasn't serving anymore, moved to a template - Nicer messages in cli and browser --- main.go | 2 +- pkg/plaid_cli/linker.go | 200 ++++++++++++++++++++++++++++++++++++---- static/link.html | 59 ------------ 3 files changed, 183 insertions(+), 78 deletions(-) delete mode 100644 static/link.html diff --git a/main.go b/main.go index c7e7677..018fcf9 100644 --- a/main.go +++ b/main.go @@ -76,7 +76,7 @@ func main() { log.Fatal(err) } - linker := plaid_cli.NewLinker(data, client) + linker := plaid_cli.NewLinker(data, client, opts) linkCommand := &cobra.Command{ Use: "link [ITEM-ID-OR-ALIAS]", diff --git a/pkg/plaid_cli/linker.go b/pkg/plaid_cli/linker.go index a968244..052e338 100644 --- a/pkg/plaid_cli/linker.go +++ b/pkg/plaid_cli/linker.go @@ -12,10 +12,11 @@ import ( ) type Linker struct { - Results chan string - Errors chan error - Client *plaid.Client - Data *Data + Results chan string + Errors chan error + Client *plaid.Client + ClientOpts plaid.ClientOptions + Data *Data } type TokenPair struct { @@ -38,7 +39,8 @@ func (l *Linker) Link(port string) (*TokenPair, error) { } func (l *Linker) link(port string, serveLink func(w http.ResponseWriter, r *http.Request)) (*TokenPair, error) { - log.Println(fmt.Sprintf("Starting Plaid Link on port %s", port)) + log.Println(fmt.Sprintf("Starting Plaid Link on port %s...", port)) + go func() { http.HandleFunc("/link", serveLink) err := http.ListenAndServe(fmt.Sprintf(":%s", port), nil) @@ -47,7 +49,9 @@ func (l *Linker) link(port string, serveLink func(w http.ResponseWriter, r *http } }() - open.Run(fmt.Sprintf("http://localhost:%s/link", port)) + url := fmt.Sprintf("http://localhost:%s/link", port) + log.Println(fmt.Sprintf("Your browser should open automatically. If it doesn't, please visit %s to continue linking!", url)) + open.Run(url) select { case err := <-l.Errors: @@ -71,12 +75,13 @@ func (l *Linker) exchange(publicToken string) (plaid.ExchangePublicTokenResponse return l.Client.ExchangePublicToken(publicToken) } -func NewLinker(data *Data, client *plaid.Client) *Linker { +func NewLinker(data *Data, client *plaid.Client, clientOpts plaid.ClientOptions) *Linker { return &Linker{ - Results: make(chan string), - Errors: make(chan error), - Client: client, - Data: data, + Results: make(chan string), + Errors: make(chan error), + Client: client, + ClientOpts: clientOpts, + Data: data, } } @@ -84,7 +89,25 @@ func handleLink(linker *Linker) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: - http.ServeFile(w, r, "./static/link.html") + t := template.New("link") + t, _ = t.Parse(linkTemplate) + + var env string + switch linker.ClientOpts.Environment { + case plaid.Development: + env = "development" + case plaid.Production: + env = "production" + case plaid.Sandbox: + env = "sandbox" + default: + env = "development" + } + d := LinkTmplData{ + PublicKey: linker.ClientOpts.PublicKey, + Environment: env, + } + t.Execute(w, d) case http.MethodPost: r.ParseForm() token := r.Form.Get("public_token") @@ -101,8 +124,15 @@ func handleLink(linker *Linker) func(w http.ResponseWriter, r *http.Request) { } } -type RelinkTemplData struct { +type LinkTmplData struct { + PublicKey string + Environment string +} + +type RelinkTmplData struct { PublicToken string + PublicKey string + Environment string } func handleRelink(linker *Linker, publicToken string) func(w http.ResponseWriter, r *http.Request) { @@ -111,8 +141,22 @@ func handleRelink(linker *Linker, publicToken string) func(w http.ResponseWriter case http.MethodGet: t := template.New("relink") t, _ = t.Parse(relinkTemplate) - d := RelinkTemplData{ + + var env string + switch linker.ClientOpts.Environment { + case plaid.Development: + env = "development" + case plaid.Production: + env = "production" + case plaid.Sandbox: + env = "sandbox" + default: + env = "development" + } + d := RelinkTmplData{ PublicToken: publicToken, + PublicKey: linker.ClientOpts.PublicKey, + Environment: env, } t.Execute(w, d) case http.MethodPost: @@ -131,7 +175,26 @@ func handleRelink(linker *Linker, publicToken string) func(w http.ResponseWriter } } -var relinkTemplate string = ` +var linkTemplate string = ` + + + @@ -143,11 +206,11 @@ var relinkTemplate string = ` // codes to initialize Link; European countries will have GDPR // consent panel countryCodes: ['US'], - env: 'development', + env: '{{ .Environment }}', // Replace with your public_key from the Dashboard - key: '880bb11f8bc9f3c1d8feb4a348f371', + key: '{{ .PublicKey }}', product: ['transactions'], - token: "{{ .PublicToken }}", + // Optional, specify a language to localize Link language: 'en', onLoad: function() { // Optional, called when Link loads @@ -169,6 +232,100 @@ var relinkTemplate string = ` // metadata contains information about the institution // that the user selected and the most recent API request IDs. // Storing this information can be helpful for support. + + document.getElementById("alert").classList.remove("hidden"); + }, + onEvent: function(eventName, metadata) { + // Optionally capture Link flow events, streamed through + // this callback as your users connect an Item to Plaid. + // For example: + // eventName = "TRANSITION_VIEW" + // metadata = { + // link_session_id: "123-abc", + // mfa_type: "questions", + // timestamp: "2017-09-14T14:42:19.350Z", + // view_name: "MFA", + // } + } + }); + + handler.open(); + + })(jQuery); + + + + + ` + +var relinkTemplate string = ` + + + + + + + + + ` diff --git a/static/link.html b/static/link.html deleted file mode 100644 index c78f7f6..0000000 --- a/static/link.html +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - -