implement gofmt and add a filewatcher in Goland

This commit is contained in:
Wouter Groeneveld 2021-04-09 21:00:54 +02:00
parent 3933e4d43b
commit d4c854ef81
28 changed files with 208 additions and 199 deletions

29
.idea/watcherTasks.xml Normal file
View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectTasksOptions">
<TaskOptions isEnabled="true">
<option name="arguments" value="fmt $FilePath$" />
<option name="checkSyntaxErrors" value="true" />
<option name="description" />
<option name="exitCodeBehavior" value="ERROR" />
<option name="fileExtension" value="go" />
<option name="immediateSync" value="false" />
<option name="name" value="go fmt" />
<option name="output" value="$FilePath$" />
<option name="outputFilters">
<array />
</option>
<option name="outputFromStdout" value="false" />
<option name="program" value="$GoExecPath$" />
<option name="runOnExternalChanges" value="false" />
<option name="scopeName" value="Project Files" />
<option name="trackOnlyRoot" value="true" />
<option name="workingDir" value="$ProjectFileDir$" />
<envs>
<env name="GOROOT" value="$GOROOT$" />
<env name="GOPATH" value="$GOPATH$" />
<env name="PATH" value="$GoBinDirs$" />
</envs>
</TaskOptions>
</component>
</project>

View File

@ -1,17 +1,15 @@
package index package index
import ( import (
"net/http"
"fmt" "fmt"
"net/http"
"brainbaking.com/go-jamming/common" "brainbaking.com/go-jamming/common"
) )
func Handle(conf *common.Config) http.HandlerFunc { func Handle(conf *common.Config) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "This is a Jamstack microservice endpoint.\nWanna start jammin' too? Go to https://github.com/wgroeneveld/go-jamming !") fmt.Fprintf(w, "This is a Jamstack microservice endpoint.\nWanna start jammin' too? Go to https://github.com/wgroeneveld/go-jamming !")
} }
} }

View File

@ -1,4 +1,3 @@
package app package app
import ( import (
@ -8,24 +7,24 @@ import (
) )
type loggingResponseWriter struct { type loggingResponseWriter struct {
http.ResponseWriter http.ResponseWriter
statusCode int statusCode int
} }
// mimic ResponseWriter's WriteHeader to capture the code // mimic ResponseWriter's WriteHeader to capture the code
func (lrw *loggingResponseWriter) WriteHeader(code int) { func (lrw *loggingResponseWriter) WriteHeader(code int) {
lrw.statusCode = code lrw.statusCode = code
lrw.ResponseWriter.WriteHeader(code) lrw.ResponseWriter.WriteHeader(code)
} }
func loggingMiddleware(next http.Handler) http.Handler { func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logWriter := &loggingResponseWriter{w, http.StatusOK} logWriter := &loggingResponseWriter{w, http.StatusOK}
next.ServeHTTP(logWriter, r) next.ServeHTTP(logWriter, r)
log.Info(). log.Info().
Str("url", r.RequestURI). Str("url", r.RequestURI).
Str("method", r.Method). Str("method", r.Method).
Int("status", logWriter.statusCode). Int("status", logWriter.statusCode).
Msg("handled") Msg("handled")
}) })
} }

View File

@ -1,9 +1,9 @@
package mf package mf
import ( import (
"brainbaking.com/go-jamming/common"
"crypto/md5" "crypto/md5"
"fmt" "fmt"
"brainbaking.com/go-jamming/common"
"net/url" "net/url"
) )
@ -17,7 +17,7 @@ func (wm *Mention) String() string {
} }
func (wm *Mention) AsPath(conf *common.Config) string { func (wm *Mention) AsPath(conf *common.Config) string {
filename := fmt.Sprintf("%x", md5.Sum([]byte("source=" + wm.Source+ ",target=" + wm.Target))) filename := fmt.Sprintf("%x", md5.Sum([]byte("source="+wm.Source+",target="+wm.Target)))
domain, _ := conf.FetchDomain(wm.Target) domain, _ := conf.FetchDomain(wm.Target)
return conf.DataPath + "/" + domain + "/" + filename + ".json" return conf.DataPath + "/" + domain + "/" + filename + ".json"
} }

View File

@ -1,10 +1,10 @@
package mf package mf
import ( import (
"brainbaking.com/go-jamming/common"
"strings" "strings"
"time" "time"
"willnorris.com/go/microformats" "willnorris.com/go/microformats"
"brainbaking.com/go-jamming/common"
) )
const ( const (
@ -12,19 +12,19 @@ const (
) )
type IndiewebAuthor struct { type IndiewebAuthor struct {
Name string `json:"name"` Name string `json:"name"`
Picture string `json:"picture"` Picture string `json:"picture"`
} }
type IndiewebData struct { type IndiewebData struct {
Author IndiewebAuthor `json:"author"` Author IndiewebAuthor `json:"author"`
Name string `json:"name"` Name string `json:"name"`
Content string `json:"content"` Content string `json:"content"`
Published string `json:"published"` Published string `json:"published"`
Url string `json:"url"` Url string `json:"url"`
IndiewebType string `json:"type"` IndiewebType string `json:"type"`
Source string `json:"source"` Source string `json:"source"`
Target string `json:"target"` Target string `json:"target"`
} }
func PublishedNow(utcOffset int) string { func PublishedNow(utcOffset int) string {
@ -134,4 +134,4 @@ func DetermineContent(hEntry *microformats.Microformat) string {
} }
contentEntry := Map(hEntry, "content")["value"] contentEntry := Map(hEntry, "content")["value"]
return shorten(contentEntry) return shorten(contentEntry)
} }

View File

@ -1,19 +1,18 @@
package pingback package pingback
import ( import (
"encoding/xml"
"github.com/rs/zerolog/log"
"brainbaking.com/go-jamming/app/mf" "brainbaking.com/go-jamming/app/mf"
"brainbaking.com/go-jamming/app/webmention/recv" "brainbaking.com/go-jamming/app/webmention/recv"
"brainbaking.com/go-jamming/common" "brainbaking.com/go-jamming/common"
"brainbaking.com/go-jamming/rest" "brainbaking.com/go-jamming/rest"
"encoding/xml"
"github.com/rs/zerolog/log"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
) )
func HandlePost(conf *common.Config) http.HandlerFunc { func HandlePost(conf *common.Config) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body) body, err := ioutil.ReadAll(r.Body)
if err != nil { if err != nil {
pingbackError(w, "Unable to read request body") pingbackError(w, "Unable to read request body")
@ -94,5 +93,3 @@ func pingbackError(w http.ResponseWriter, msg string) {
w.WriteHeader(200) w.WriteHeader(200)
w.Write([]byte(xml)) w.Write([]byte(xml))
} }

View File

@ -1,9 +1,9 @@
package send package send
import ( import (
"github.com/rs/zerolog/log"
"brainbaking.com/go-jamming/app/mf" "brainbaking.com/go-jamming/app/mf"
"brainbaking.com/go-jamming/rest" "brainbaking.com/go-jamming/rest"
"github.com/rs/zerolog/log"
"strings" "strings"
) )

View File

@ -1,9 +1,9 @@
package send package send
import ( import (
"github.com/stretchr/testify/assert"
"brainbaking.com/go-jamming/app/mf" "brainbaking.com/go-jamming/app/mf"
"brainbaking.com/go-jamming/mocks" "brainbaking.com/go-jamming/mocks"
"github.com/stretchr/testify/assert"
"testing" "testing"
) )
@ -35,4 +35,4 @@ func TestSendPingbackToEndpoint(t *testing.T) {
Target: "target", Target: "target",
}) })
assert.Equal(t, expectedXml, capturedBody) assert.Equal(t, expectedXml, capturedBody)
} }

View File

@ -1,9 +1,9 @@
package pingback package pingback
import ( import (
"brainbaking.com/go-jamming/common"
"encoding/xml" "encoding/xml"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"brainbaking.com/go-jamming/common"
"testing" "testing"
) )
@ -16,8 +16,8 @@ var conf *common.Config = &common.Config{
func TestValidate(t *testing.T) { func TestValidate(t *testing.T) {
cases := []struct { cases := []struct {
label string label string
xml string xml string
expected bool expected bool
}{ }{
{ {
@ -175,4 +175,4 @@ func TestValidate(t *testing.T) {
assert.Equal(t, tc.expected, result) assert.Equal(t, tc.expected, result)
}) })
} }
} }

View File

@ -15,11 +15,11 @@ import "encoding/xml"
</param> </param>
</params> </params>
</methodCall> </methodCall>
*/ */
type XmlRPCMethodCall struct { type XmlRPCMethodCall struct {
XMLName xml.Name `xml:"methodCall"` XMLName xml.Name `xml:"methodCall"`
MethodName string `xml:"methodName"` MethodName string `xml:"methodName"`
Params XmlRPCParams `xml:"params"` Params XmlRPCParams `xml:"params"`
} }
func (rpc *XmlRPCMethodCall) Source() string { func (rpc *XmlRPCMethodCall) Source() string {
@ -31,15 +31,15 @@ func (rpc *XmlRPCMethodCall) Target() string {
} }
type XmlRPCParams struct { type XmlRPCParams struct {
XMLName xml.Name `xml:"params"` XMLName xml.Name `xml:"params"`
Parameters []XmlRPCParam `xml:"param"` Parameters []XmlRPCParam `xml:"param"`
} }
type XmlRPCParam struct { type XmlRPCParam struct {
XMLName xml.Name `xml:"param"` XMLName xml.Name `xml:"param"`
Value XmlRPCValue `xml:"value"` Value XmlRPCValue `xml:"value"`
} }
type XmlRPCValue struct { type XmlRPCValue struct {
String string `xml:"string"` String string `xml:"string"`
} }

View File

@ -27,4 +27,4 @@ func TestMarshallValidXMLRPC(t *testing.T) {
assert.Equal(t, "pingback.ping", rpc.MethodName) assert.Equal(t, "pingback.ping", rpc.MethodName)
assert.Equal(t, "https://brainbaking.com/kristien.html", rpc.Params.Parameters[0].Value.String) assert.Equal(t, "https://brainbaking.com/kristien.html", rpc.Params.Parameters[0].Value.String)
assert.Equal(t, "https://kristienthoelen.be/2021/03/22/de-stadia-van-een-burn-out-in-welk-stadium-zit-jij/", rpc.Params.Parameters[1].Value.String) assert.Equal(t, "https://kristienthoelen.be/2021/03/22/de-stadia-van-een-burn-out-in-welk-stadium-zit-jij/", rpc.Params.Parameters[1].Value.String)
} }

View File

@ -1,4 +1,3 @@
package app package app
import ( import (
@ -19,4 +18,3 @@ func (s *server) routes() {
s.router.HandleFunc("/webmention/{domain}/{token}", s.authorizedOnly(webmention.HandleGet(cnf))).Methods("GET") s.router.HandleFunc("/webmention/{domain}/{token}", s.authorizedOnly(webmention.HandleGet(cnf))).Methods("GET")
s.router.HandleFunc("/webmention/{domain}/{token}", s.authorizedOnly(webmention.HandlePut(cnf))).Methods("PUT") s.router.HandleFunc("/webmention/{domain}/{token}", s.authorizedOnly(webmention.HandlePut(cnf))).Methods("PUT")
} }

View File

@ -1,4 +1,3 @@
package app package app
import ( import (
@ -13,33 +12,35 @@ import (
type server struct { type server struct {
router *mux.Router router *mux.Router
conf *common.Config conf *common.Config
} }
// mimicing NotFound: https://golang.org/src/net/http/server.go?s=64787:64830#L2076 // mimicing NotFound: https://golang.org/src/net/http/server.go?s=64787:64830#L2076
func unauthorized(w http.ResponseWriter, r *http.Request) { http.Error(w, "401 unauthorized", http.StatusUnauthorized) } func unauthorized(w http.ResponseWriter, r *http.Request) {
http.Error(w, "401 unauthorized", http.StatusUnauthorized)
}
func (s *server) authorizedOnly(h http.HandlerFunc) http.HandlerFunc { func (s *server) authorizedOnly(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
if vars["token"] != s.conf.Token || !s.conf.IsAnAllowedDomain(vars["domain"]) { if vars["token"] != s.conf.Token || !s.conf.IsAnAllowedDomain(vars["domain"]) {
unauthorized(w, r) unauthorized(w, r)
return return
} }
h(w, r) h(w, r)
} }
} }
func Start() { func Start() {
r := mux.NewRouter() r := mux.NewRouter()
config := common.Configure() config := common.Configure()
config.SetupDataDirs() config.SetupDataDirs()
server := &server{router: r, conf: config} server := &server{router: r, conf: config}
server.routes() server.routes()
http.Handle("/", r) http.Handle("/", r)
r.Use(loggingMiddleware) r.Use(loggingMiddleware)
log.Info().Int("port", server.conf.Port).Msg("Serving...") log.Info().Int("port", server.conf.Port).Msg("Serving...")
http.ListenAndServe(":" + strconv.Itoa(server.conf.Port), nil) http.ListenAndServe(":"+strconv.Itoa(server.conf.Port), nil)
} }

View File

@ -1,17 +1,17 @@
package app package app
import ( import (
"brainbaking.com/go-jamming/common"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"brainbaking.com/go-jamming/common"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
) )
var conf = &common.Config{ var conf = &common.Config{
Token: "boemsjakkalakka", Token: "boemsjakkalakka",
AllowedWebmentionSources: []string{ "http://ewelja.be" }, AllowedWebmentionSources: []string{"http://ewelja.be"},
} }
func TestAuthorizedOnlyUnauthorizedWithWrongToken(t *testing.T) { func TestAuthorizedOnlyUnauthorizedWithWrongToken(t *testing.T) {
@ -26,7 +26,7 @@ func TestAuthorizedOnlyUnauthorizedWithWrongToken(t *testing.T) {
r, _ := http.NewRequest("PUT", "/whatever", nil) r, _ := http.NewRequest("PUT", "/whatever", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
r = mux.SetURLVars(r, map[string]string{ r = mux.SetURLVars(r, map[string]string{
"token": "invalid", "token": "invalid",
"domain": conf.AllowedWebmentionSources[0], "domain": conf.AllowedWebmentionSources[0],
}) })
@ -46,7 +46,7 @@ func TestAuthorizedOnlyUnauthorizedWithWrongDomain(t *testing.T) {
r, _ := http.NewRequest("PUT", "/whatever", nil) r, _ := http.NewRequest("PUT", "/whatever", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
r = mux.SetURLVars(r, map[string]string{ r = mux.SetURLVars(r, map[string]string{
"token": conf.Token, "token": conf.Token,
"domain": "https://sexymoddafokkas.be", "domain": "https://sexymoddafokkas.be",
}) })
@ -66,7 +66,7 @@ func TestAuthorizedOnlyOkIfTokenAndDomainMatch(t *testing.T) {
r, _ := http.NewRequest("PUT", "/whatever", nil) r, _ := http.NewRequest("PUT", "/whatever", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
r = mux.SetURLVars(r, map[string]string{ r = mux.SetURLVars(r, map[string]string{
"token": conf.Token, "token": conf.Token,
"domain": conf.AllowedWebmentionSources[0], "domain": conf.AllowedWebmentionSources[0],
}) })

View File

@ -1,12 +1,11 @@
package webmention package webmention
import ( import (
"fmt"
"github.com/gorilla/mux"
"brainbaking.com/go-jamming/app/mf" "brainbaking.com/go-jamming/app/mf"
"brainbaking.com/go-jamming/app/webmention/recv" "brainbaking.com/go-jamming/app/webmention/recv"
"brainbaking.com/go-jamming/app/webmention/send" "brainbaking.com/go-jamming/app/webmention/send"
"fmt"
"github.com/gorilla/mux"
"net/http" "net/http"
"brainbaking.com/go-jamming/common" "brainbaking.com/go-jamming/common"
@ -16,13 +15,13 @@ import (
var httpClient = &rest.HttpClient{} var httpClient = &rest.HttpClient{}
func HandleGet(conf *common.Config) http.HandlerFunc { func HandleGet(conf *common.Config) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
fmt.Println("handling get") fmt.Println("handling get")
} }
} }
func HandlePut(conf *common.Config) http.HandlerFunc { func HandlePut(conf *common.Config) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
since := getSinceQueryParam(r) since := getSinceQueryParam(r)
domain := mux.Vars(r)["domain"] domain := mux.Vars(r)["domain"]
@ -45,30 +44,29 @@ func getSinceQueryParam(r *http.Request) string {
} }
func HandlePost(conf *common.Config) http.HandlerFunc { func HandlePost(conf *common.Config) http.HandlerFunc {
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) {
rest.BadRequest(w) rest.BadRequest(w)
return return
} }
target := r.FormValue("target")
if !isValidTargetUrl(target, httpClient) {
rest.BadRequest(w)
return
}
wm := mf.Mention{ target := r.FormValue("target")
Source: r.FormValue("source"), if !isValidTargetUrl(target, httpClient) {
Target: target, rest.BadRequest(w)
} return
recv := &recv.Receiver{ }
RestClient: httpClient,
Conf: conf,
}
go recv.Receive(wm) wm := mf.Mention{
rest.Accept(w) Source: r.FormValue("source"),
} Target: target,
}
recv := &recv.Receiver{
RestClient: httpClient,
Conf: conf,
}
go recv.Receive(wm)
rest.Accept(w)
}
} }

View File

@ -1,11 +1,10 @@
package recv package recv
import ( import (
"encoding/json"
"brainbaking.com/go-jamming/app/mf" "brainbaking.com/go-jamming/app/mf"
"brainbaking.com/go-jamming/common" "brainbaking.com/go-jamming/common"
"brainbaking.com/go-jamming/rest" "brainbaking.com/go-jamming/rest"
"encoding/json"
"io/fs" "io/fs"
"io/ioutil" "io/ioutil"
"os" "os"
@ -16,9 +15,8 @@ import (
"willnorris.com/go/microformats" "willnorris.com/go/microformats"
) )
// used as a "class" to iject dependencies, just to be able to test. Do NOT like htis.
// 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.
// Is there a better way? e.g. in validate, I just pass rest.Client as an arg. Not great either.
type Receiver struct { type Receiver struct {
RestClient rest.Client RestClient rest.Client
Conf *common.Config Conf *common.Config
@ -29,7 +27,7 @@ func (recv *Receiver) Receive(wm mf.Mention) {
body, geterr := recv.RestClient.GetBody(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")
recv.deletePossibleOlderWebmention(wm) recv.deletePossibleOlderWebmention(wm)
return return
} }
@ -50,7 +48,6 @@ func getHEntry(data *microformats.Data) *microformats.Microformat {
return nil return nil
} }
func (recv *Receiver) processSourceBody(body string, wm mf.Mention) { func (recv *Receiver) processSourceBody(body string, wm mf.Mention) {
if !strings.Contains(body, wm.Target) { if !strings.Contains(body, wm.Target) {
log.Warn().Str("target", wm.Target).Msg("ABORT: no mention of target found in html src of source!") log.Warn().Str("target", wm.Target).Msg("ABORT: no mention of target found in html src of source!")
@ -92,14 +89,14 @@ func (recv *Receiver) parseBodyAsIndiewebSite(hEntry *microformats.Microformat,
return &mf.IndiewebData{ return &mf.IndiewebData{
Name: name, Name: name,
Author: mf.IndiewebAuthor{ Author: mf.IndiewebAuthor{
Name: mf.DetermineAuthorName(hEntry), Name: mf.DetermineAuthorName(hEntry),
Picture: pic, Picture: pic,
}, },
Content: mf.DetermineContent(hEntry), Content: mf.DetermineContent(hEntry),
Url: mf.DetermineUrl(hEntry, wm.Source), Url: mf.DetermineUrl(hEntry, wm.Source),
Published: mf.DeterminePublishedDate(hEntry, recv.Conf.UtcOffset), Published: mf.DeterminePublishedDate(hEntry, recv.Conf.UtcOffset),
Source: wm.Source, Source: wm.Source,
Target: wm.Target, Target: wm.Target,
IndiewebType: mfType, IndiewebType: mfType,
} }
} }
@ -115,12 +112,12 @@ func (recv *Receiver) parseBodyAsNonIndiewebSite(body string, wm mf.Mention) *mf
Author: mf.IndiewebAuthor{ Author: mf.IndiewebAuthor{
Name: wm.Source, Name: wm.Source,
}, },
Name: title, Name: title,
Content: title, Content: title,
Published: mf.PublishedNow(recv.Conf.UtcOffset), Published: mf.PublishedNow(recv.Conf.UtcOffset),
Url: wm.Source, Url: wm.Source,
IndiewebType: "mention", IndiewebType: "mention",
Source: wm.Source, Source: wm.Source,
Target: wm.Target, Target: wm.Target,
} }
} }

View File

@ -1,10 +1,9 @@
package recv package recv
import ( import (
"brainbaking.com/go-jamming/app/mf"
"errors" "errors"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"brainbaking.com/go-jamming/app/mf"
"io/ioutil" "io/ioutil"
"os" "os"
"testing" "testing"
@ -15,13 +14,12 @@ import (
) )
var conf = &common.Config{ var conf = &common.Config{
AllowedWebmentionSources: []string { AllowedWebmentionSources: []string{
"jefklakscodex.com", "jefklakscodex.com",
}, },
DataPath: "testdata", DataPath: "testdata",
} }
func TestConvertWebmentionToPath(t *testing.T) { func TestConvertWebmentionToPath(t *testing.T) {
wm := mf.Mention{ wm := mf.Mention{
Source: "https://brainbaking.com", Source: "https://brainbaking.com",
@ -37,7 +35,7 @@ func TestConvertWebmentionToPath(t *testing.T) {
func writeSomethingTo(filename string) { func writeSomethingTo(filename string) {
file, _ := os.Create(filename) file, _ := os.Create(filename)
file.WriteString("lolz") file.WriteString("lolz")
defer file.Close() defer file.Close()
} }
func TestReceive(t *testing.T) { func TestReceive(t *testing.T) {
@ -45,7 +43,7 @@ func TestReceive(t *testing.T) {
label string label string
wm mf.Mention wm mf.Mention
json string json string
} { }{
{ {
label: "receive a Webmention bookmark via twitter", label: "receive a Webmention bookmark via twitter",
wm: mf.Mention{ wm: mf.Mention{
@ -137,7 +135,7 @@ func TestReceiveTargetDoesNotExistAnymoreDeletesPossiblyOlderWebmention(t *testi
GetBodyFunc: func(url string) (string, error) { GetBodyFunc: func(url string) (string, error) {
return "", errors.New("whoops") return "", errors.New("whoops")
}, },
} }
receiver := &Receiver{ receiver := &Receiver{
Conf: conf, Conf: conf,
RestClient: client, RestClient: client,
@ -181,4 +179,3 @@ func TestProcessSourceBodyAbortsIfNoMentionOfTargetFoundInSourceHtml(t *testing.
receiver.processSourceBody("<html>my nice body</html>", wm) receiver.processSourceBody("<html>my nice body</html>", wm)
assert.NoFileExists(t, wm.AsPath(conf)) assert.NoFileExists(t, wm.AsPath(conf))
} }

View File

@ -1,11 +1,11 @@
package send package send
import ( import (
"github.com/rs/zerolog/log"
"brainbaking.com/go-jamming/app/mf" "brainbaking.com/go-jamming/app/mf"
"brainbaking.com/go-jamming/app/pingback/send" "brainbaking.com/go-jamming/app/pingback/send"
"brainbaking.com/go-jamming/common" "brainbaking.com/go-jamming/common"
"brainbaking.com/go-jamming/rest" "brainbaking.com/go-jamming/rest"
"github.com/rs/zerolog/log"
) )
type Sender struct { type Sender struct {

View File

@ -1,4 +1,3 @@
package webmention package webmention
import ( import (

View File

@ -1,10 +1,9 @@
package webmention package webmention
import ( import (
"testing"
"errors" "errors"
"net/http" "net/http"
"testing"
"brainbaking.com/go-jamming/common" "brainbaking.com/go-jamming/common"
"brainbaking.com/go-jamming/mocks" "brainbaking.com/go-jamming/mocks"
@ -17,14 +16,18 @@ type httpReqMock struct {
type httpHeaderMock struct { type httpHeaderMock struct {
contentType string contentType string
} }
func (mock *httpHeaderMock) Get(key string) string { func (mock *httpHeaderMock) Get(key string) string {
return mock.contentType return mock.contentType
} }
func (mock *httpReqMock) FormValue(key string) string { func (mock *httpReqMock) FormValue(key string) string {
switch key { switch key {
case "source": return mock.source case "source":
case "target": return mock.target return mock.source
default: return "" case "target":
return mock.target
default:
return ""
} }
} }
func buildHttpReq(source string, target string) *httpReqMock { func buildHttpReq(source string, target string) *httpReqMock {
@ -38,12 +41,12 @@ var config = common.Configure()
func TestValidate(t *testing.T) { func TestValidate(t *testing.T) {
cases := []struct { cases := []struct {
label string label string
source string source string
target string target string
contentType string contentType string
expected bool expected bool
} { }{
{ {
"is valid if source and target https urls", "is valid if source and target https urls",
"http://brainbaking.com/bla1", "http://brainbaking.com/bla1",
@ -105,14 +108,14 @@ func TestValidate(t *testing.T) {
for _, tc := range cases { for _, tc := range cases {
t.Run(tc.label, func(t *testing.T) { t.Run(tc.label, func(t *testing.T) {
httpReq := buildHttpReq(tc.source, tc.target) httpReq := buildHttpReq(tc.source, tc.target)
httpHeader := &httpHeaderMock{ contentType: tc.contentType } httpHeader := &httpHeaderMock{contentType: tc.contentType}
actual := validate(httpReq, httpHeader, config) actual := validate(httpReq, httpHeader, config)
if actual != tc.expected { if actual != tc.expected {
t.Fatalf("got %v, want %v", actual, tc.expected) t.Fatalf("got %v, want %v", actual, tc.expected)
} }
}) })
} }
} }
func TestIsValidTargetUrlFalseIfGetFails(t *testing.T) { func TestIsValidTargetUrlFalseIfGetFails(t *testing.T) {

View File

@ -1,4 +1,3 @@
package common package common
import ( import (
@ -11,11 +10,11 @@ import (
) )
type Config struct { type Config struct {
Port int Port int
Token string Token string
UtcOffset int UtcOffset int
DataPath string DataPath string
AllowedWebmentionSources []string AllowedWebmentionSources []string
DisallowedWebmentionDomains []string DisallowedWebmentionDomains []string
} }
@ -39,15 +38,15 @@ func (c *Config) FetchDomain(url string) (string, error) {
func (c *Config) SetupDataDirs() { func (c *Config) SetupDataDirs() {
for _, domain := range c.AllowedWebmentionSources { for _, domain := range c.AllowedWebmentionSources {
os.MkdirAll(c.DataPath + "/" + domain, os.ModePerm) os.MkdirAll(c.DataPath+"/"+domain, os.ModePerm)
log.Info().Str("allowedDomain", domain).Msg("Configured") log.Info().Str("allowedDomain", domain).Msg("Configured")
} }
} }
func Configure() (c *Config) { func Configure() (c *Config) {
portstr := os.Getenv("PORT") portstr := os.Getenv("PORT")
port, err := strconv.Atoi(portstr) port, err := strconv.Atoi(portstr)
if err != nil { if err != nil {
port = 1337 port = 1337
} }
token := os.Getenv("TOKEN") token := os.Getenv("TOKEN")
@ -56,12 +55,12 @@ func Configure() (c *Config) {
} }
c = &Config{ c = &Config{
Port: port, Port: port,
Token: token, Token: token,
UtcOffset: 60, UtcOffset: 60,
DataPath: "data", DataPath: "data",
AllowedWebmentionSources: []string{ "brainbaking.com", "jefklakscodex.com" }, AllowedWebmentionSources: []string{"brainbaking.com", "jefklakscodex.com"},
DisallowedWebmentionDomains: []string{ "youtube.com" }, DisallowedWebmentionDomains: []string{"youtube.com"},
} }
return return
} }

View File

@ -3,7 +3,7 @@ package common
import "testing" import "testing"
func TestIncludesElemInArrayTrue(t *testing.T) { func TestIncludesElemInArrayTrue(t *testing.T) {
arr := []string{ "one", "two"} arr := []string{"one", "two"}
result := Includes(arr, "two") result := Includes(arr, "two")
if result != true { if result != true {
@ -12,7 +12,7 @@ func TestIncludesElemInArrayTrue(t *testing.T) {
} }
func TestIncludesElemNotInArrayFalse(t *testing.T) { func TestIncludesElemNotInArrayFalse(t *testing.T) {
arr := []string{ "one", "two"} arr := []string{"one", "two"}
result := Includes(arr, "three") result := Includes(arr, "three")
if result != false { if result != false {

13
main.go
View File

@ -1,4 +1,3 @@
package main package main
import ( import (
@ -6,15 +5,15 @@ import (
"brainbaking.com/go-jamming/app" "brainbaking.com/go-jamming/app"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
func main() { func main() {
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
// TODO this should only be enabled in local mode. Fix with config? // TODO this should only be enabled in local mode. Fix with config?
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
log.Debug().Msg("Let's a go!") log.Debug().Msg("Let's a go!")
app.Start() app.Start()
} }

View File

@ -1,21 +1,20 @@
package mocks package mocks
import ( import (
"strings"
"testing"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"strings"
"testing"
) )
// neat trick! https://medium.com/@matryer/meet-moq-easily-mock-interfaces-in-go-476444187d10 // neat trick! https://medium.com/@matryer/meet-moq-easily-mock-interfaces-in-go-476444187d10
type RestClientMock struct { type RestClientMock struct {
GetFunc func(string) (*http.Response, error) GetFunc func(string) (*http.Response, error)
GetBodyFunc func(string) (string, error) GetBodyFunc func(string) (string, error)
PostFunc func(string, string, string) error PostFunc func(string, string, string) error
} }
// although these are still requied to match the rest.Client interface. // although these are still requied to match the rest.Client interface.
func (m *RestClientMock) Get(url string) (*http.Response, error) { func (m *RestClientMock) Get(url string) (*http.Response, error) {
return m.GetFunc(url) return m.GetFunc(url)
} }

View File

@ -2,9 +2,9 @@ package main
import ( import (
"fmt" "fmt"
"log"
"net/http"
"io/ioutil" "io/ioutil"
"log"
"net/http"
) )
func mainz() { func mainz() {
@ -14,11 +14,11 @@ func mainz() {
log.Fatalln(err) log.Fatalln(err)
} }
body, err2 := ioutil.ReadAll(resp.Body) body, err2 := ioutil.ReadAll(resp.Body)
if err2 != nil { if err2 != nil {
log.Fatalln(err) log.Fatalln(err)
} }
fmt.Printf("tis ditte") fmt.Printf("tis ditte")
fmt.Printf("%s", body) fmt.Printf("%s", body)
} }

View File

@ -1,4 +1,3 @@
package rest package rest
import ( import (
@ -32,9 +31,9 @@ func (client *HttpClient) GetBody(url string) (string, error) {
return "", geterr return "", geterr
} }
if resp.StatusCode < 200 || resp.StatusCode > 299 { if resp.StatusCode < 200 || resp.StatusCode > 299 {
return "", fmt.Errorf("Status code for %s is not OK (%d)", url, resp.StatusCode) return "", fmt.Errorf("Status code for %s is not OK (%d)", url, resp.StatusCode)
} }
defer resp.Body.Close() defer resp.Body.Close()
body, readerr := ioutil.ReadAll(resp.Body) body, readerr := ioutil.ReadAll(resp.Body)
@ -45,7 +44,6 @@ func (client *HttpClient) GetBody(url string) (string, error) {
return string(body), nil return string(body), nil
} }
func (client *HttpClient) Get(url string) (*http.Response, error) { func (client *HttpClient) Get(url string) (*http.Response, error) {
return http.Get(url) return http.Get(url)
} }

View File

@ -1,4 +1,3 @@
package rest package rest
import ( import (

View File

@ -1,4 +1,3 @@
package rest package rest
// great, these are needed to do the structural typing for the tests... // great, these are needed to do the structural typing for the tests...