Skip to content

Commit

Permalink
Restructure project to add tests (#1)
Browse files Browse the repository at this point in the history
- moves auth logic into 'client' package
- replaces debug logging statements with logrus
- updates .gitignore
- adds initial test
  • Loading branch information
ivan3bx authored Nov 15, 2022
1 parent 15cf0a6 commit 5650cb8
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 149 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
.vscode
dist/
123 changes: 123 additions & 0 deletions client/authorization.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package client

import (
"context"
"errors"
"fmt"
"net"
"net/http"
"os"
"os/signal"
"syscall"
"time"

"github.com/mattn/go-mastodon"
"github.com/pkg/browser"
log "github.com/sirupsen/logrus"
)

func RegisterNewClient(serverName string) (*mastodon.Client, error) {
done := make(chan os.Signal, 1)

// Start temporary server to capture auth code
var authCode string

// Listen on default port
listener := *newListener()
listenerPort := listener.Addr().(*net.TCPAddr).Port
listenerHost := fmt.Sprintf("%s:%v", "localhost", listenerPort)

go func() {
mux := http.NewServeMux()

// Handle client-side redirect to extract 'auth' code, and close window
mux.HandleFunc("/auth", func(w http.ResponseWriter, r *http.Request) {
authCode = r.URL.Query().Get("code")
w.Write([]byte(`
<html>
<body>
<h2>It is safe to close this window..</h2>
</body>
</html>
`))
done <- os.Interrupt
})

log.Debugf("listening for auth response on port %v", listenerPort)
if err := http.Serve(listener, mux); err != nil && err != http.ErrServerClosed {
log.Fatalf("authentication listener failed to start: %s\n", err)
}
}()

signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
startTimeout(done)

app, err := mastodon.RegisterApp(context.Background(), &mastodon.AppConfig{
Server: serverURL(serverName),
ClientName: "Links From Bookmarks",
Scopes: "read:bookmarks read:favourites",
Website: "https://github.com/ivan3bx/proma",
RedirectURIs: fmt.Sprintf("http://%s/auth", listenerHost),
})

if err != nil {
return nil, err
}

log.Debugf("client-id : %s", app.ClientID)
log.Debugf("client-secret: %s", app.ClientSecret)

if err := browser.OpenURL(app.AuthURI); err != nil {
return nil, err
}

<-done

log.Debug("listener stopped")

if authCode == "" {
return nil, errors.New("auth code was not present, or was blank")
}

// Create mastodon client
client := mastodon.NewClient(&mastodon.Config{
Server: serverURL(serverName),
ClientID: app.ClientID,
ClientSecret: app.ClientSecret,
})

if err = client.AuthenticateToken(context.Background(), authCode, app.RedirectURI); err != nil {
return nil, err
}

log.Infof("authenticated to %s\n", client.Config.Server)

return client, nil
}

func startTimeout(done chan<- os.Signal) {
timer := time.NewTimer(time.Second * 60)

go func() {
<-timer.C
log.Debug("timeout exceeded. canceling authentication")
done <- os.Interrupt
}()
}

func serverURL(serverName string) string {
return fmt.Sprintf("https://%s", serverName)
}

func newListener() *net.Listener {
listener, err := net.Listen("tcp", "localhost:3334")

if err != nil {
// attempt to use next available port
listener, err = net.Listen("tcp", "localhost:0")
if err != nil {
panic(err)
}
}
return &listener
}
25 changes: 25 additions & 0 deletions client/authorization_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package client

import "testing"

func TestServerURL(t *testing.T) {
testCases := []struct {
name string
input string
expected string
}{
{
name: "success",
input: "mastodon.social",
expected: "https://mastodon.social",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
actual := serverURL(tc.input)
if actual != tc.expected {
t.Errorf("expected URL to match (%v / %v)\n", tc.expected, actual)
}
})
}
}
135 changes: 10 additions & 125 deletions cmd/authenticate.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,11 @@ package cmd

import (
"bufio"
"context"
"errors"
"fmt"
"net"
"net/http"
"os"
"os/signal"
"syscall"
"time"

"github.com/fatih/color"
"github.com/mattn/go-mastodon"
"github.com/pkg/browser"
"github.com/ivan3bx/proma/client"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

Expand All @@ -38,17 +30,17 @@ Examples:
and saves an AccessToken to the config file.
`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Server: %s\n", yellow(serverName))
fmt.Printf("Re-run this command with '-server' to use a different server.\n\n")
fmt.Printf("This will launch a browser window in order to authorize this app.\n")
fmt.Println("Hit <Enter> to continue...")
log.Infof("Server: %s\n", yellow(serverName))
log.Infof("Re-run this command with '-server' to use a different server.\n\n")
log.Infof("This will launch a browser window in order to authorize this app.\n")
log.Infof("Hit <Enter> to continue...")
bufio.NewReader(os.Stdin).ReadBytes('\n')

var err error
client, err = RegisterNewClient()
c, err := client.RegisterNewClient(serverName)
cobra.CheckErr(err)

v.Set(serverName, client.Config)
v.Set(serverName, c.Config)
cobra.CheckErr(v.WriteConfig())

},
Expand All @@ -58,116 +50,9 @@ func init() {
rootCmd.AddCommand(authenticateCmd)
}

func RegisterNewClient() (*mastodon.Client, error) {
done := make(chan os.Signal, 1)

// Start temporary server to capture auth code
var authCode string

// Listen on default port
listener := *newListener()
listenerPort := listener.Addr().(*net.TCPAddr).Port
listenerHost := fmt.Sprintf("%s:%v", "localhost", listenerPort)

go func() {
mux := http.NewServeMux()

// Handle client-side redirect to extract 'auth' code, and close window
mux.HandleFunc("/auth", func(w http.ResponseWriter, r *http.Request) {
authCode = r.URL.Query().Get("code")
w.Write([]byte(`
<html>
<body>
<h2>It is safe to close this window..</h2>
</body>
</html>
`))
done <- os.Interrupt
})

debugf("listening for auth response on port %v", listenerPort)
if err := http.Serve(listener, mux); err != nil && err != http.ErrServerClosed {
fmt.Printf("authentication listener failed to start: %s\n", err)
os.Exit(1)
}
}()

signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
startTimeout(done)

app, err := mastodon.RegisterApp(context.Background(), &mastodon.AppConfig{
Server: serverURL(),
ClientName: "Links From Bookmarks",
Scopes: "read:bookmarks read:favourites",
Website: "https://github.com/ivan3bx/proma",
RedirectURIs: fmt.Sprintf("http://%s/auth", listenerHost),
})

if err != nil {
return nil, err
}

debugf("client-id : %s", app.ClientID)
debugf("client-secret: %s", app.ClientSecret)

if err := browser.OpenURL(app.AuthURI); err != nil {
return nil, err
}

<-done

debug("listener stopped")

if authCode == "" {
return nil, errors.New("auth code was not present, or was blank")
}

// Create mastodon client
client := mastodon.NewClient(&mastodon.Config{
Server: serverURL(),
ClientID: app.ClientID,
ClientSecret: app.ClientSecret,
})

if err = client.AuthenticateToken(context.Background(), authCode, app.RedirectURI); err != nil {
return nil, err
}

fmt.Printf("authenticated to %s\n", client.Config.Server)

return client, nil
}

func startTimeout(done chan<- os.Signal) {
timer := time.NewTimer(time.Second * 60)

go func() {
<-timer.C
debug("timeout exceeded. canceling authentication")
done <- os.Interrupt
}()
}

func serverURL() string {
return fmt.Sprintf("https://%s", serverName)
}

func newListener() *net.Listener {
listener, err := net.Listen("tcp", "localhost:3334")

if err != nil {
// attempt to use next available port
listener, err = net.Listen("tcp", "localhost:0")
if err != nil {
panic(err)
}
}
return &listener
}

func requireClient(cmd *cobra.Command, args []string) {
if client == nil {
fmt.Printf("See 'auth -h' to authenticate\n")
if mClient == nil {
log.Infof("See 'auth -h' to authenticate\n")
os.Exit(1)
}
}
6 changes: 4 additions & 2 deletions cmd/links.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"os"
"strings"

log "github.com/sirupsen/logrus"

"github.com/PuerkitoBio/goquery"
"github.com/mattn/go-mastodon"
"github.com/spf13/cobra"
Expand All @@ -29,7 +31,7 @@ var linksCmd = &cobra.Command{
Collects links embedded in the content of your saved bookmarks.`,
PreRun: requireClient,
Run: func(cmd *cobra.Command, args []string) {
st, err := client.GetBookmarks(cmd.Context(), &mastodon.Pagination{Limit: limit})
st, err := mClient.GetBookmarks(cmd.Context(), &mastodon.Pagination{Limit: limit})
cobra.CheckErr(err)

outputLinks(st)
Expand Down Expand Up @@ -60,7 +62,7 @@ func outputLinks(status []*mastodon.Status) {
if href, ok := s.Attr("href"); ok {

if strings.HasPrefix(href, origin) {
debug("skipping internal href: ", href)
log.Debug("skipping internal href: ", href)
return
}

Expand Down
Loading

0 comments on commit 5650cb8

Please sign in to comment.