attempt at primitive DI, mock own rest client
This commit is contained in:
parent
95abdcc7f0
commit
f82a581731
|
@ -6,6 +6,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/wgroeneveld/go-jamming/common"
|
"github.com/wgroeneveld/go-jamming/common"
|
||||||
|
"github.com/wgroeneveld/go-jamming/rest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func HandleGet(conf *common.Config) http.HandlerFunc {
|
func HandleGet(conf *common.Config) http.HandlerFunc {
|
||||||
|
@ -21,26 +22,31 @@ func HandlePut(conf *common.Config) http.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandlePost(conf *common.Config) http.HandlerFunc {
|
func HandlePost(conf *common.Config) http.HandlerFunc {
|
||||||
|
httpClient := &rest.HttpClient{}
|
||||||
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
r.ParseForm()
|
r.ParseForm()
|
||||||
if !validate(r, r.Header, conf) {
|
if !validate(r, r.Header, conf) {
|
||||||
common.BadRequest(w)
|
rest.BadRequest(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
target := r.FormValue("target")
|
target := r.FormValue("target")
|
||||||
if !isValidTargetUrl(target) {
|
if !isValidTargetUrl(target, httpClient) {
|
||||||
common.BadRequest(w)
|
rest.BadRequest(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
wm := &webmention{
|
wm := webmention{
|
||||||
source: r.FormValue("source"),
|
source: r.FormValue("source"),
|
||||||
target: target,
|
target: target,
|
||||||
}
|
}
|
||||||
|
recv := &receiver{
|
||||||
|
restClient: httpClient,
|
||||||
|
}
|
||||||
|
|
||||||
go wm.receive()
|
go recv.receive(wm)
|
||||||
common.Accept(w)
|
rest.Accept(w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ package webmention
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/wgroeneveld/go-jamming/common"
|
"github.com/wgroeneveld/go-jamming/rest"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
@ -18,23 +18,29 @@ func (wm *webmention) String() string {
|
||||||
return fmt.Sprintf("source: %s, target: %s", wm.source, wm.target)
|
return fmt.Sprintf("source: %s, target: %s", wm.source, wm.target)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wm *webmention) receive() {
|
// used as a "class" to iject dependencies, just to be able to test. Do NOT like htis.
|
||||||
|
// Is there a better way? e.g. in validate, I just pass rest.Client as an arg. Not great either.
|
||||||
|
type receiver struct {
|
||||||
|
restClient rest.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (recv *receiver) receive(wm webmention) {
|
||||||
log.Info().Str("webmention", wm.String()).Msg("OK: looks valid")
|
log.Info().Str("webmention", wm.String()).Msg("OK: looks valid")
|
||||||
body, geterr := common.Get(wm.source)
|
body, geterr := recv.restClient.GetBody(wm.source)
|
||||||
|
|
||||||
if geterr != nil {
|
if geterr != nil {
|
||||||
log.Warn().Str("source", wm.source).Msg(" ABORT: invalid url")
|
log.Warn().Str("source", wm.source).Msg(" ABORT: invalid url")
|
||||||
wm.deletePossibleOlderWebmention()
|
recv.deletePossibleOlderWebmention(wm)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
wm.processSourceBody(body)
|
recv.processSourceBody(body, wm)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wm *webmention) deletePossibleOlderWebmention() {
|
func (recv *receiver) deletePossibleOlderWebmention(wm webmention) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wm *webmention) processSourceBody(body string) {
|
func (recv *receiver) processSourceBody(body string, wm webmention) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,9 @@ package webmention
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/wgroeneveld/go-jamming/common"
|
"github.com/wgroeneveld/go-jamming/common"
|
||||||
|
"github.com/wgroeneveld/go-jamming/rest"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
@ -24,16 +24,8 @@ func isValidDomain(url string, conf *common.Config) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// great, these are needed to do the structural typing for the tests...
|
func isValidTargetUrl(url string, httpClient rest.Client) bool {
|
||||||
type httpReq interface {
|
_, err := httpClient.Get(url)
|
||||||
FormValue(key string) string
|
|
||||||
}
|
|
||||||
type httpHeader interface {
|
|
||||||
Get(key string) string
|
|
||||||
}
|
|
||||||
|
|
||||||
func isValidTargetUrl(url string) bool {
|
|
||||||
_, err := http.Get(url)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn().Str("target", url).Msg("Invalid target URL")
|
log.Warn().Str("target", url).Msg("Invalid target URL")
|
||||||
return false
|
return false
|
||||||
|
@ -41,7 +33,7 @@ func isValidTargetUrl(url string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func validate(r httpReq, h httpHeader, conf *common.Config) bool {
|
func validate(r rest.HttpReq, h rest.HttpHeader, conf *common.Config) bool {
|
||||||
return h.Get("Content-Type") == "application/x-www-form-urlencoded" &&
|
return h.Get("Content-Type") == "application/x-www-form-urlencoded" &&
|
||||||
isValidUrl(r.FormValue("source")) &&
|
isValidUrl(r.FormValue("source")) &&
|
||||||
isValidUrl(r.FormValue("target")) &&
|
isValidUrl(r.FormValue("target")) &&
|
||||||
|
|
|
@ -3,6 +3,8 @@ package webmention
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/wgroeneveld/go-jamming/common"
|
"github.com/wgroeneveld/go-jamming/common"
|
||||||
)
|
)
|
||||||
|
@ -109,7 +111,34 @@ func TestValidate(t *testing.T) {
|
||||||
t.Fatalf("got %v, want %v", actual, tc.expected)
|
t.Fatalf("got %v, want %v", actual, tc.expected)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type restClientMock struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *restClientMock) Get(url string) (*http.Response, error) {
|
||||||
|
if url == "failing" {
|
||||||
|
return nil, errors.New("whoops")
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
func (client *restClientMock) GetBody(url string) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsValidTargetUrlFalseIfGetFails(t *testing.T) {
|
||||||
|
client := &restClientMock{}
|
||||||
|
result := isValidTargetUrl("failing", client)
|
||||||
|
if result != false {
|
||||||
|
t.Fatalf("expected to fail")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsValidTargetUrlTrueIfGetSucceeds(t *testing.T) {
|
||||||
|
client := &restClientMock{}
|
||||||
|
result := isValidTargetUrl("valid stuff!", client)
|
||||||
|
if result != true {
|
||||||
|
t.Fatalf("expected to succeed")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,22 @@
|
||||||
|
|
||||||
package common
|
package rest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func BadRequest(w http.ResponseWriter) {
|
type Client interface {
|
||||||
http.Error(w, "400 bad request", http.StatusBadRequest)
|
Get(url string) (*http.Response, error)
|
||||||
|
GetBody(url string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Accept(w http.ResponseWriter) {
|
type HttpClient struct {
|
||||||
w.WriteHeader(202)
|
|
||||||
w.Write([]byte("Thanks, bro. Will send these webmentions soon, pinky swear!"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// something like this? https://freshman.tech/snippets/go/http-response-to-string/
|
// something like this? https://freshman.tech/snippets/go/http-response-to-string/
|
||||||
func Get(url string) (string, error) {
|
func (client *HttpClient) GetBody(url string) (string, error) {
|
||||||
resp, geterr := http.Get(url)
|
resp, geterr := http.Get(url)
|
||||||
if geterr != nil {
|
if geterr != nil {
|
||||||
return "", geterr
|
return "", geterr
|
||||||
|
@ -35,3 +34,8 @@ func Get(url string) (string, error) {
|
||||||
|
|
||||||
return string(body), nil
|
return string(body), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (client *HttpClient) Get(url string) (*http.Response, error) {
|
||||||
|
return http.Get(url)
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
package rest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BadRequest(w http.ResponseWriter) {
|
||||||
|
http.Error(w, "400 bad request", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Accept(w http.ResponseWriter) {
|
||||||
|
w.WriteHeader(202)
|
||||||
|
w.Write([]byte("Thanks, bro. Will send these webmentions soon, pinky swear!"))
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
package rest
|
||||||
|
|
||||||
|
// great, these are needed to do the structural typing for the tests...
|
||||||
|
type HttpReq interface {
|
||||||
|
FormValue(key string) string
|
||||||
|
}
|
||||||
|
type HttpHeader interface {
|
||||||
|
Get(key string) string
|
||||||
|
}
|
Loading…
Reference in New Issue