Improve source code
All checks were successful
the build was successful

This commit is contained in:
Peter Stuifzand 2019-03-07 20:55:25 +01:00
parent 3fe9d65cdf
commit 70f5fb82f9
Signed by: peter
GPG Key ID: 374322D56E5209E8
10 changed files with 159 additions and 87 deletions

18
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,18 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.1.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-merge-conflict
- id: check-added-large-files
- repo: https://github.com/dnephin/pre-commit-golang
rev: master
hooks:
- id: go-vet
- id: go-fmt
- id: go-lint
- id: go-unit-tests

View File

@ -1,19 +1,5 @@
/* /*
Microsub server Package main contains the main command for the Microsub server.
Copyright (C) 2018 Peter Stuifzand
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package main package main
@ -77,7 +63,7 @@ func WithAuth(handler http.Handler, b *memoryBackend) http.Handler {
return return
} }
if token.Me != b.Me { if token.Me != b.Me { // FIXME: Me should be part of the request
log.Printf("Missing \"me\" in token response: %#v\n", token) log.Printf("Missing \"me\" in token response: %#v\n", token)
http.Error(w, "Wrong me", 403) http.Error(w, "Wrong me", 403)
return return

View File

@ -1,19 +1,20 @@
/* /*
Microsub server Package main runs the microsub server
Copyright (C) 2018 Peter Stuifzand
This program is free software: you can redistribute it and/or modify Copyright (C) 2018 Peter Stuifzand
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, This program is free software: you can redistribute it and/or modify
but WITHOUT ANY WARRANTY; without even the implied warranty of it under the terms of the GNU General Public License as published by
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the the Free Software Foundation, either version 3 of the License, or
GNU General Public License for more details. (at your option) any later version.
You should have received a copy of the GNU General Public License This program is distributed in the hope that it will be useful,
along with this program. If not, see <http://www.gnu.org/licenses/>. but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package main package main
@ -42,6 +43,7 @@ import (
"willnorris.com/go/microformats" "willnorris.com/go/microformats"
) )
// DefaultPrio is the priority value for new channels
const DefaultPrio = 9999999 const DefaultPrio = 9999999
type memoryBackend struct { type memoryBackend struct {
@ -51,10 +53,10 @@ type memoryBackend struct {
Channels map[string]microsub.Channel Channels map[string]microsub.Channel
Feeds map[string][]microsub.Feed Feeds map[string][]microsub.Feed
Settings map[string]channelSetting Settings map[string]channelSetting
NextUid int NextUID int
Me string Me string // FIXME: should be removed
TokenEndpoint string TokenEndpoint string // FIXME: should be removed
AuthEnabled bool AuthEnabled bool
ticker *time.Ticker ticker *time.Ticker
@ -68,6 +70,7 @@ type channelSetting struct {
IncludeRegex string IncludeRegex string
} }
// Debug interface for easy of use in other packages
type Debug interface { type Debug interface {
Debug() Debug()
} }
@ -176,7 +179,8 @@ func createMemoryBackend() {
backend.Channels[c.UID] = c backend.Channels[c.UID] = c
} }
backend.NextUid = 1000000 backend.NextUID = 1000000
// FIXME: can't be used in Backend
backend.Me = "https://example.com/" backend.Me = "https://example.com/"
backend.lock.Unlock() backend.lock.Unlock()
@ -443,24 +447,24 @@ func (b *memoryBackend) Search(query string) ([]microsub.Feed, error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
fetchUrl, err := url.Parse(u) fetchURL, err := url.Parse(u)
md := microformats.Parse(resp.Body, fetchUrl) md := microformats.Parse(resp.Body, fetchURL)
if err != nil { if err != nil {
log.Printf("Error while fetching %s: %v\n", u, err) log.Printf("Error while fetching %s: %v\n", u, err)
continue continue
} }
feedResp, err := Fetch2(fetchUrl.String()) feedResp, err := Fetch2(fetchURL.String())
if err != nil { if err != nil {
log.Printf("Error in fetch of %s - %v\n", fetchUrl, err) log.Printf("Error in fetch of %s - %v\n", fetchURL, err)
continue continue
} }
defer feedResp.Body.Close() defer feedResp.Body.Close()
// TODO: Combine FeedHeader and FeedItems so we can use it here // TODO: Combine FeedHeader and FeedItems so we can use it here
parsedFeed, err := fetch.FeedHeader(&fetch2{}, fetchUrl.String(), feedResp.Header.Get("Content-Type"), feedResp.Body) parsedFeed, err := fetch.FeedHeader(&fetch2{}, fetchURL.String(), feedResp.Header.Get("Content-Type"), feedResp.Body)
if err != nil { if err != nil {
log.Printf("Error in parse of %s - %v\n", fetchUrl, err) log.Printf("Error in parse of %s - %v\n", fetchURL, err)
continue continue
} }
@ -706,7 +710,7 @@ func Fetch2(fetchURL string) (*http.Response, error) {
client := http.Client{} client := http.Client{}
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return nil, fmt.Errorf("fetch failed: %s", u, err) return nil, fmt.Errorf("fetch failed: %s: %s", u, err)
} }
defer resp.Body.Close() defer resp.Body.Close()
@ -724,10 +728,11 @@ func Fetch2(fetchURL string) (*http.Response, error) {
} }
func (b *memoryBackend) createChannel(name string) microsub.Channel { func (b *memoryBackend) createChannel(name string) microsub.Channel {
uid := fmt.Sprintf("%012d", b.NextUid) uid := fmt.Sprintf("%012d", b.NextUID)
channel := microsub.Channel{ channel := microsub.Channel{
UID: uid, UID: uid,
Name: name, Name: name,
Unread: microsub.Unread{microsub.UnreadCount, false, 0},
} }
return channel return channel
} }
@ -737,7 +742,7 @@ func (b *memoryBackend) setChannel(channel microsub.Channel) {
defer b.lock.Unlock() defer b.lock.Unlock()
b.Channels[channel.UID] = channel b.Channels[channel.UID] = channel
b.Feeds[channel.UID] = []microsub.Feed{} b.Feeds[channel.UID] = []microsub.Feed{}
b.NextUid++ b.NextUID++
} }
func updateChannelInRedis(conn redis.Conn, uid string, prio int) { func updateChannelInRedis(conn redis.Conn, uid string, prio int) {

View File

@ -291,8 +291,10 @@ func (timeline *redisStreamTimeline) Items(before, after string) (microsub.Timel
} }
return microsub.Timeline{ return microsub.Timeline{
Items: items, Items: items,
Paging: microsub.Pagination{}, Paging: microsub.Pagination{
After: items[len(items)-1].ID,
},
}, nil }, nil
} }

View File

@ -13,6 +13,7 @@ import (
"p83.nl/go/ekster/pkg/microsub" "p83.nl/go/ekster/pkg/microsub"
) )
// Client is a HTTP client for Microsub
type Client struct { type Client struct {
Me *url.URL Me *url.URL
MicrosubEndpoint *url.URL MicrosubEndpoint *url.URL
@ -120,6 +121,7 @@ func (c *Client) microsubPostFormRequest(action string, args map[string]string,
return res, err return res, err
} }
// ChannelsGetList gets the channels from a Microsub server
func (c *Client) ChannelsGetList() ([]microsub.Channel, error) { func (c *Client) ChannelsGetList() ([]microsub.Channel, error) {
args := make(map[string]string) args := make(map[string]string)
res, err := c.microsubGetRequest("channels", args) res, err := c.microsubGetRequest("channels", args)
@ -146,6 +148,7 @@ func (c *Client) ChannelsGetList() ([]microsub.Channel, error) {
return channels.Channels, err return channels.Channels, err
} }
// TimelineGet gets a timeline from a Microsub server
func (c *Client) TimelineGet(before, after, channel string) (microsub.Timeline, error) { func (c *Client) TimelineGet(before, after, channel string) (microsub.Timeline, error) {
args := make(map[string]string) args := make(map[string]string)
args["after"] = after args["after"] = after
@ -172,6 +175,7 @@ func (c *Client) TimelineGet(before, after, channel string) (microsub.Timeline,
return timeline, nil return timeline, nil
} }
// PreviewURL gets a Timeline for a url from a Microsub server
func (c *Client) PreviewURL(url string) (microsub.Timeline, error) { func (c *Client) PreviewURL(url string) (microsub.Timeline, error) {
args := make(map[string]string) args := make(map[string]string)
args["url"] = url args["url"] = url
@ -338,7 +342,3 @@ func (c *Client) MarkRead(channel string, uids []string) error {
res.Body.Close() res.Body.Close()
return nil return nil
} }
func (c *Client) AddEventListener(el microsub.EventListener) error {
panic("implement me")
}

View File

@ -1,21 +1,3 @@
/*
Microsub server
Copyright (C) 2018 Peter Stuifzand
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Package microsub describes the protocol methods of the Microsub protocol // Package microsub describes the protocol methods of the Microsub protocol
package microsub package microsub
@ -34,11 +16,13 @@ import (
block / unblock block / unblock
*/ */
// Constants for Unread
const ( const (
UnreadBool = 0 UnreadBool = 0
UnreadCount = 1 UnreadCount = 1
) )
// Unread is a special int/bool value for the JSON response
type Unread struct { type Unread struct {
Type int Type int
Unread bool Unread bool

View File

@ -0,0 +1,84 @@
package microsub
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_UnmarshalUnreadBool(t *testing.T) {
var x Unread
err := json.Unmarshal([]byte("false"), &x)
if assert.NoError(t, err) {
assert.Equal(t, UnreadBool, x.Type)
assert.False(t, x.Unread)
}
}
func Test_UnmarshalUnreadBoolTrue(t *testing.T) {
var x Unread
err := json.Unmarshal([]byte("true"), &x)
if assert.NoError(t, err) {
assert.Equal(t, UnreadBool, x.Type)
assert.True(t, x.Unread)
}
}
func Test_UnmarshalUnreadIntZero(t *testing.T) {
var x Unread
err := json.Unmarshal([]byte("0"), &x)
if assert.NoError(t, err) {
assert.Equal(t, UnreadCount, x.Type)
assert.Equal(t, 0, x.UnreadCount)
}
}
func Test_UnmarshalUnreadIntNonZero(t *testing.T) {
var x Unread
err := json.Unmarshal([]byte("209449"), &x)
if assert.NoError(t, err) {
assert.Equal(t, UnreadCount, x.Type)
assert.Equal(t, 209449, x.UnreadCount)
}
}
func Test_MarshalUnreadEmpty(t *testing.T) {
x := Unread{}
bytes, err := json.Marshal(x)
if assert.NoError(t, err) {
assert.Equal(t, "false", string(bytes))
}
}
func Test_MarshalUnreadBoolFalse(t *testing.T) {
x := Unread{Type: UnreadBool, Unread: false}
bytes, err := json.Marshal(x)
if assert.NoError(t, err) {
assert.Equal(t, "false", string(bytes))
}
}
func Test_MarshalUnreadBoolTrue(t *testing.T) {
x := Unread{Type: UnreadBool, Unread: true}
bytes, err := json.Marshal(x)
if assert.NoError(t, err) {
assert.Equal(t, "true", string(bytes))
}
}
func Test_MarshalUnreadIntZero(t *testing.T) {
x := Unread{Type: UnreadCount, UnreadCount: 0}
bytes, err := json.Marshal(x)
if assert.NoError(t, err) {
assert.Equal(t, "0", string(bytes))
}
}
func Test_MarshalUnreadIntNonZero(t *testing.T) {
x := Unread{Type: UnreadCount, UnreadCount: 1884844}
bytes, err := json.Marshal(x)
if assert.NoError(t, err) {
assert.Equal(t, "1884844", string(bytes))
}
}

View File

@ -1,19 +1,6 @@
/* /*
ekster - microsub server Package server contains the microsub server itself. It implements http.Handler.
Copyright (C) 2018 Peter Stuifzand It follows the spec at https://indieweb.org/Microsub-spec.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package server package server
@ -31,6 +18,7 @@ var (
entryRegex = regexp.MustCompile("^entry\\[\\d+\\]$") entryRegex = regexp.MustCompile("^entry\\[\\d+\\]$")
) )
// Constants used for the responses
const ( const (
OutputContentType = "application/json; charset=utf-8" OutputContentType = "application/json; charset=utf-8"
) )
@ -51,11 +39,14 @@ func respondJSON(w http.ResponseWriter, value interface{}) {
} }
} }
// NewMicrosubHandler is the main entry point for the microsub server
// It returns a handler for HTTP and a broker that will send events.
func NewMicrosubHandler(backend microsub.Microsub) (http.Handler, *Broker) { func NewMicrosubHandler(backend microsub.Microsub) (http.Handler, *Broker) {
broker := NewServer() broker := NewServer()
return &microsubHandler{backend, broker}, broker return &microsubHandler{backend, broker}, broker
} }
// Methods required by http.Handler
func (h *microsubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *microsubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
r.ParseForm() r.ParseForm()

View File

@ -14,7 +14,7 @@ import (
func createServerClient() (*httptest.Server, *client.Client) { func createServerClient() (*httptest.Server, *client.Client) {
backend := &NullBackend{} backend := &NullBackend{}
handler := NewMicrosubHandler(backend) handler, _ := NewMicrosubHandler(backend)
server := httptest.NewServer(handler) server := httptest.NewServer(handler)

View File

@ -25,10 +25,6 @@ import (
type NullBackend struct { type NullBackend struct {
} }
func (b *NullBackend) AddEventListener(el microsub.EventListener) error {
panic("implement me")
}
// ChannelsGetList gets no channels // ChannelsGetList gets no channels
func (b *NullBackend) ChannelsGetList() ([]microsub.Channel, error) { func (b *NullBackend) ChannelsGetList() ([]microsub.Channel, error) {
return []microsub.Channel{ return []microsub.Channel{
@ -66,26 +62,31 @@ func (b *NullBackend) TimelineGet(before, after, channel string) (microsub.Timel
}, nil }, nil
} }
// FollowGetList implements the follow list command
func (b *NullBackend) FollowGetList(uid string) ([]microsub.Feed, error) { func (b *NullBackend) FollowGetList(uid string) ([]microsub.Feed, error) {
return []microsub.Feed{ return []microsub.Feed{
{Name: "test", Type: "feed", URL: "https://example.com/"}, {Name: "test", Type: "feed", URL: "https://example.com/"},
}, nil }, nil
} }
// FollowURL follows a new url
func (b *NullBackend) FollowURL(uid string, url string) (microsub.Feed, error) { func (b *NullBackend) FollowURL(uid string, url string) (microsub.Feed, error) {
return microsub.Feed{Type: "feed", URL: url}, nil return microsub.Feed{Type: "feed", URL: url}, nil
} }
// UnfollowURL unfollows a url
func (b *NullBackend) UnfollowURL(uid string, url string) error { func (b *NullBackend) UnfollowURL(uid string, url string) error {
return nil return nil
} }
// Search search for a query and return an example list of feeds
func (b *NullBackend) Search(query string) ([]microsub.Feed, error) { func (b *NullBackend) Search(query string) ([]microsub.Feed, error) {
return []microsub.Feed{ return []microsub.Feed{
{"feed", "https://example.com/", "Example", "test.jpg", "test", microsub.Card{}}, {"feed", "https://example.com/", "Example", "test.jpg", "test", microsub.Card{}},
}, nil }, nil
} }
// PreviewURL shows an empty feed
func (b *NullBackend) PreviewURL(url string) (microsub.Timeline, error) { func (b *NullBackend) PreviewURL(url string) (microsub.Timeline, error) {
return microsub.Timeline{ return microsub.Timeline{
Paging: microsub.Pagination{}, Paging: microsub.Pagination{},
@ -93,6 +94,7 @@ func (b *NullBackend) PreviewURL(url string) (microsub.Timeline, error) {
}, nil }, nil
} }
// MarkRead marks no items as read
func (b *NullBackend) MarkRead(channel string, uids []string) error { func (b *NullBackend) MarkRead(channel string, uids []string) error {
return nil return nil
} }