Compare commits

...

2 Commits

Author SHA1 Message Date
6677bd95ab Use errors in protocol
- Rewrite all api's and clients to use the new calls which allows errors
in the responses
2018-07-07 16:40:04 +02:00
bf645f33ef Move microsub to pkg/microsub 2018-07-07 16:03:49 +02:00
10 changed files with 316 additions and 153 deletions

View File

@ -7,9 +7,9 @@ import (
"net/url" "net/url"
"os" "os"
"github.com/pstuifzand/ekster/microsub"
"github.com/pstuifzand/ekster/pkg/client" "github.com/pstuifzand/ekster/pkg/client"
"github.com/pstuifzand/ekster/pkg/indieauth" "github.com/pstuifzand/ekster/pkg/indieauth"
"github.com/pstuifzand/ekster/pkg/microsub"
) )
func init() { func init() {
@ -173,7 +173,11 @@ Commands:
} }
if len(commands) == 1 && commands[0] == "channels" { if len(commands) == 1 && commands[0] == "channels" {
channels := sub.ChannelsGetList() channels, err := sub.ChannelsGetList()
if err != nil {
log.Fatalf("An error occurred: %s\n", err)
}
for _, ch := range channels { for _, ch := range channels {
fmt.Printf("%-20s %s\n", ch.UID, ch.Name) fmt.Printf("%-20s %s\n", ch.UID, ch.Name)
} }
@ -181,7 +185,10 @@ Commands:
if len(commands) == 2 && commands[0] == "channels" { if len(commands) == 2 && commands[0] == "channels" {
name := commands[1] name := commands[1]
channel := sub.ChannelsCreate(name) channel, err := sub.ChannelsCreate(name)
if err != nil {
log.Fatalf("An error occurred: %s\n", err)
}
fmt.Printf("%s\n", channel.UID) fmt.Printf("%s\n", channel.UID)
} }
@ -189,11 +196,17 @@ Commands:
uid := commands[1] uid := commands[1]
if uid == "-delete" { if uid == "-delete" {
uid = commands[2] uid = commands[2]
sub.ChannelsDelete(uid) err := sub.ChannelsDelete(uid)
if err != nil {
log.Fatalf("An error occurred: %s\n", err)
}
fmt.Printf("Channel %s deleted\n", uid) fmt.Printf("Channel %s deleted\n", uid)
} else { } else {
name := commands[2] name := commands[2]
channel := sub.ChannelsUpdate(uid, name) channel, err := sub.ChannelsUpdate(uid, name)
if err != nil {
log.Fatalf("An error occurred: %s\n", err)
}
fmt.Printf("Channel updated %s %s\n", channel.Name, channel.UID) fmt.Printf("Channel updated %s %s\n", channel.Name, channel.UID)
} }
} }
@ -202,13 +215,18 @@ Commands:
channel := commands[1] channel := commands[1]
var timeline microsub.Timeline var timeline microsub.Timeline
var err error
if len(commands) == 4 && commands[2] == "-after" { if len(commands) == 4 && commands[2] == "-after" {
timeline = sub.TimelineGet("", commands[3], channel) timeline, err = sub.TimelineGet("", commands[3], channel)
} else if len(commands) == 4 && commands[2] == "-before" { } else if len(commands) == 4 && commands[2] == "-before" {
timeline = sub.TimelineGet(commands[3], "", channel) timeline, err = sub.TimelineGet(commands[3], "", channel)
} else { } else {
timeline = sub.TimelineGet("", "", channel) timeline, err = sub.TimelineGet("", "", channel)
}
if err != nil {
log.Fatalf("An error occurred: %s\n", err)
} }
for _, item := range timeline.Items { for _, item := range timeline.Items {
@ -220,7 +238,10 @@ Commands:
if len(commands) == 2 && commands[0] == "search" { if len(commands) == 2 && commands[0] == "search" {
query := commands[1] query := commands[1]
feeds := sub.Search(query) feeds, err := sub.Search(query)
if err != nil {
log.Fatalf("An error occurred: %s\n", err)
}
for _, feed := range feeds { for _, feed := range feeds {
fmt.Println(feed.Name, " ", feed.URL) fmt.Println(feed.Name, " ", feed.URL)
@ -229,8 +250,11 @@ Commands:
if len(commands) == 2 && commands[0] == "preview" { if len(commands) == 2 && commands[0] == "preview" {
url := commands[1] url := commands[1]
timeline := sub.PreviewURL(url) timeline, err := sub.PreviewURL(url)
if err != nil {
log.Fatalf("An error occurred: %s\n", err)
}
for _, item := range timeline.Items { for _, item := range timeline.Items {
showItem(&item) showItem(&item)
} }
@ -238,7 +262,10 @@ Commands:
if len(commands) == 2 && commands[0] == "follow" { if len(commands) == 2 && commands[0] == "follow" {
uid := commands[1] uid := commands[1]
feeds := sub.FollowGetList(uid) feeds, err := sub.FollowGetList(uid)
if err != nil {
log.Fatalf("An error occurred: %s\n", err)
}
for _, feed := range feeds { for _, feed := range feeds {
fmt.Println(feed.Name, " ", feed.URL) fmt.Println(feed.Name, " ", feed.URL)
} }
@ -247,13 +274,20 @@ Commands:
if len(commands) == 3 && commands[0] == "follow" { if len(commands) == 3 && commands[0] == "follow" {
uid := commands[1] uid := commands[1]
url := commands[2] url := commands[2]
sub.FollowURL(uid, url) _, err := sub.FollowURL(uid, url)
if err != nil {
log.Fatalf("An error occurred: %s\n", err)
}
// NOTE(peter): should we show the returned feed here?
} }
if len(commands) == 3 && commands[0] == "unfollow" { if len(commands) == 3 && commands[0] == "unfollow" {
uid := commands[1] uid := commands[1]
url := commands[2] url := commands[2]
sub.UnfollowURL(uid, url) err := sub.UnfollowURL(uid, url)
if err != nil {
log.Fatalf("An error occurred: %s\n", err)
}
} }
} }

View File

@ -34,7 +34,7 @@ import (
"time" "time"
"github.com/garyburd/redigo/redis" "github.com/garyburd/redigo/redis"
"github.com/pstuifzand/ekster/microsub" "github.com/pstuifzand/ekster/pkg/microsub"
"willnorris.com/go/microformats" "willnorris.com/go/microformats"
) )

View File

@ -28,7 +28,7 @@ import (
"time" "time"
"github.com/garyburd/redigo/redis" "github.com/garyburd/redigo/redis"
"github.com/pstuifzand/ekster/microsub" "github.com/pstuifzand/ekster/pkg/microsub"
"github.com/pstuifzand/ekster/pkg/util" "github.com/pstuifzand/ekster/pkg/util"
"github.com/pstuifzand/ekster/pkg/websub" "github.com/pstuifzand/ekster/pkg/websub"
) )

View File

@ -29,8 +29,8 @@ import (
"time" "time"
"github.com/garyburd/redigo/redis" "github.com/garyburd/redigo/redis"
"github.com/pstuifzand/ekster/microsub"
"github.com/pstuifzand/ekster/pkg/feedbin" "github.com/pstuifzand/ekster/pkg/feedbin"
"github.com/pstuifzand/ekster/pkg/microsub"
"willnorris.com/go/microformats" "willnorris.com/go/microformats"
) )
@ -138,7 +138,7 @@ func createMemoryBackend() microsub.Microsub {
} }
// ChannelsGetList gets channels // ChannelsGetList gets channels
func (b *memoryBackend) ChannelsGetList() []microsub.Channel { func (b *memoryBackend) ChannelsGetList() ([]microsub.Channel, error) {
conn := pool.Get() conn := pool.Get()
defer conn.Close() defer conn.Close()
@ -156,11 +156,11 @@ func (b *memoryBackend) ChannelsGetList() []microsub.Channel {
} }
} }
} }
return channels return channels, nil
} }
// ChannelsCreate creates a channels // ChannelsCreate creates a channels
func (b *memoryBackend) ChannelsCreate(name string) microsub.Channel { func (b *memoryBackend) ChannelsCreate(name string) (microsub.Channel, error) {
defer b.save() defer b.save()
conn := pool.Get() conn := pool.Get()
@ -178,22 +178,23 @@ func (b *memoryBackend) ChannelsCreate(name string) microsub.Channel {
conn.Do("SADD", "channels", uid) conn.Do("SADD", "channels", uid)
conn.Do("SETNX", "channel_sortorder_"+uid, 99999) conn.Do("SETNX", "channel_sortorder_"+uid, 99999)
return channel
return channel, nil
} }
// ChannelsUpdate updates a channels // ChannelsUpdate updates a channels
func (b *memoryBackend) ChannelsUpdate(uid, name string) microsub.Channel { func (b *memoryBackend) ChannelsUpdate(uid, name string) (microsub.Channel, error) {
defer b.save() defer b.save()
if c, e := b.Channels[uid]; e { if c, e := b.Channels[uid]; e {
c.Name = name c.Name = name
b.Channels[uid] = c b.Channels[uid] = c
return c return c, nil
} }
return microsub.Channel{} return microsub.Channel{}, fmt.Errorf("Channel %s does not exist", uid)
} }
// ChannelsDelete deletes a channel // ChannelsDelete deletes a channel
func (b *memoryBackend) ChannelsDelete(uid string) { func (b *memoryBackend) ChannelsDelete(uid string) error {
defer b.save() defer b.save()
conn := pool.Get() conn := pool.Get()
@ -204,6 +205,8 @@ func (b *memoryBackend) ChannelsDelete(uid string) {
delete(b.Channels, uid) delete(b.Channels, uid)
delete(b.Feeds, uid) delete(b.Feeds, uid)
return nil
} }
func mapToAuthor(result map[string]string) *microsub.Card { func mapToAuthor(result map[string]string) *microsub.Card {
@ -391,7 +394,7 @@ func (b *memoryBackend) run() {
}() }()
} }
func (b *memoryBackend) TimelineGet(before, after, channel string) microsub.Timeline { func (b *memoryBackend) TimelineGet(before, after, channel string) (microsub.Timeline, error) {
conn := pool.Get() conn := pool.Get()
defer conn.Close() defer conn.Close()
@ -430,11 +433,14 @@ func (b *memoryBackend) TimelineGet(before, after, channel string) microsub.Time
return microsub.Timeline{ return microsub.Timeline{
Paging: microsub.Pagination{}, Paging: microsub.Pagination{},
Items: items, Items: items,
} }, nil
} }
log.Printf("TimelineGet %s\n", channel) log.Printf("TimelineGet %s\n", channel)
feeds := b.FollowGetList(channel) feeds, err := b.FollowGetList(channel)
if err != nil {
return microsub.Timeline{}, err
}
log.Println(feeds) log.Println(feeds)
items := []microsub.Item{} items := []microsub.Item{}
@ -476,11 +482,10 @@ func (b *memoryBackend) TimelineGet(before, after, channel string) microsub.Time
) )
if err != nil { if err != nil {
log.Println(err)
return microsub.Timeline{ return microsub.Timeline{
Paging: microsub.Pagination{}, Paging: microsub.Pagination{},
Items: items, Items: items,
} }, err
} }
if len(itemScores) >= 2 { if len(itemScores) >= 2 {
@ -502,6 +507,7 @@ func (b *memoryBackend) TimelineGet(before, after, channel string) microsub.Time
item := microsub.Item{} item := microsub.Item{}
err := json.Unmarshal(obj, &item) err := json.Unmarshal(obj, &item)
if err != nil { if err != nil {
// FIXME: what should we do if one of the items doen't unmarshal?
log.Println(err) log.Println(err)
continue continue
} }
@ -516,7 +522,7 @@ func (b *memoryBackend) TimelineGet(before, after, channel string) microsub.Time
return microsub.Timeline{ return microsub.Timeline{
Paging: paging, Paging: paging,
Items: items, Items: items,
} }, nil
} }
//panic if s is not a slice //panic if s is not a slice
@ -553,17 +559,17 @@ func reverseSlice(s interface{}) {
// return false // return false
// } // }
func (b *memoryBackend) FollowGetList(uid string) []microsub.Feed { func (b *memoryBackend) FollowGetList(uid string) ([]microsub.Feed, error) {
return b.Feeds[uid] return b.Feeds[uid], nil
} }
func (b *memoryBackend) FollowURL(uid string, url string) microsub.Feed { func (b *memoryBackend) FollowURL(uid string, url string) (microsub.Feed, error) {
defer b.save() defer b.save()
feed := microsub.Feed{Type: "feed", URL: url} feed := microsub.Feed{Type: "feed", URL: url}
resp, err := b.Fetch3(uid, feed.URL) resp, err := b.Fetch3(uid, feed.URL)
if err != nil { if err != nil {
return feed return feed, err
} }
defer resp.Body.Close() defer resp.Body.Close()
@ -571,10 +577,10 @@ func (b *memoryBackend) FollowURL(uid string, url string) microsub.Feed {
b.ProcessContent(uid, feed.URL, resp.Header.Get("Content-Type"), resp.Body) b.ProcessContent(uid, feed.URL, resp.Header.Get("Content-Type"), resp.Body)
return feed return feed, nil
} }
func (b *memoryBackend) UnfollowURL(uid string, url string) { func (b *memoryBackend) UnfollowURL(uid string, url string) error {
defer b.save() defer b.save()
index := -1 index := -1
for i, f := range b.Feeds[uid] { for i, f := range b.Feeds[uid] {
@ -587,6 +593,8 @@ func (b *memoryBackend) UnfollowURL(uid string, url string) {
feeds := b.Feeds[uid] feeds := b.Feeds[uid]
b.Feeds[uid] = append(feeds[:index], feeds[index+1:]...) b.Feeds[uid] = append(feeds[:index], feeds[index+1:]...)
} }
return nil
} }
func checkURL(u string) bool { func checkURL(u string) bool {
@ -625,7 +633,7 @@ func getPossibleURLs(query string) []string {
return urls return urls
} }
func (b *memoryBackend) Search(query string) []microsub.Feed { func (b *memoryBackend) Search(query string) ([]microsub.Feed, error) {
urls := getPossibleURLs(query) urls := getPossibleURLs(query)
feeds := []microsub.Feed{} feeds := []microsub.Feed{}
@ -684,27 +692,25 @@ func (b *memoryBackend) Search(query string) []microsub.Feed {
} }
} }
return feeds return feeds, nil
} }
func (b *memoryBackend) PreviewURL(previewURL string) microsub.Timeline { func (b *memoryBackend) PreviewURL(previewURL string) (microsub.Timeline, error) {
resp, err := Fetch2(previewURL) resp, err := Fetch2(previewURL)
if err != nil { if err != nil {
log.Printf("Error while fetching %s: %v\n", previewURL, err) return microsub.Timeline{}, fmt.Errorf("error while fetching %s: %v", previewURL, err)
return microsub.Timeline{}
} }
items, err := b.feedItems(previewURL, resp.Header.Get("content-type"), resp.Body) items, err := b.feedItems(previewURL, resp.Header.Get("content-type"), resp.Body)
if err != nil { if err != nil {
log.Printf("Error while fetching %s: %v\n", previewURL, err) return microsub.Timeline{}, fmt.Errorf("error while fetching %s: %v", previewURL, err)
return microsub.Timeline{}
} }
return microsub.Timeline{ return microsub.Timeline{
Items: items, Items: items,
} }, nil
} }
func (b *memoryBackend) MarkRead(channel string, uids []string) { func (b *memoryBackend) MarkRead(channel string, uids []string) error {
conn := pool.Get() conn := pool.Get()
defer conn.Close() defer conn.Close()
@ -720,6 +726,7 @@ func (b *memoryBackend) MarkRead(channel string, uids []string) {
if _, err := conn.Do("SADD", args...); err != nil { if _, err := conn.Do("SADD", args...); err != nil {
log.Printf("Marking read for channel %s has failed\n", channel) log.Printf("Marking read for channel %s has failed\n", channel)
return err
} }
zchannelKey := fmt.Sprintf("zchannel:%s:posts", channel) zchannelKey := fmt.Sprintf("zchannel:%s:posts", channel)
@ -727,6 +734,7 @@ func (b *memoryBackend) MarkRead(channel string, uids []string) {
if _, err := conn.Do("ZREM", args...); err != nil { if _, err := conn.Do("ZREM", args...); err != nil {
log.Printf("Marking read for channel %s has failed\n", channel) log.Printf("Marking read for channel %s has failed\n", channel)
return err
} }
unread, _ := redis.Int(conn.Do("ZCARD", zchannelKey)) unread, _ := redis.Int(conn.Do("ZCARD", zchannelKey))
@ -741,4 +749,6 @@ func (b *memoryBackend) MarkRead(channel string, uids []string) {
} }
log.Printf("Marking read success for %s %v\n", channel, itemUIDs) log.Printf("Marking read success for %s %v\n", channel, itemUIDs)
return nil
} }

View File

@ -9,7 +9,7 @@ import (
"strings" "strings"
"github.com/garyburd/redigo/redis" "github.com/garyburd/redigo/redis"
"github.com/pstuifzand/ekster/microsub" "github.com/pstuifzand/ekster/pkg/microsub"
"willnorris.com/go/microformats" "willnorris.com/go/microformats"
) )

View File

@ -8,7 +8,7 @@ import (
"os" "os"
"github.com/garyburd/redigo/redis" "github.com/garyburd/redigo/redis"
"github.com/pstuifzand/ekster/microsub" "github.com/pstuifzand/ekster/pkg/microsub"
) )
type microsubHandler struct { type microsubHandler struct {
@ -49,34 +49,67 @@ func (h *microsubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
values := r.URL.Query() values := r.URL.Query()
action := values.Get("action") action := values.Get("action")
if action == "channels" { if action == "channels" {
channels := h.Backend.ChannelsGetList() channels, err := h.Backend.ChannelsGetList()
if err != nil {
http.Error(w, err.Error(), 500)
return
}
jw := json.NewEncoder(w) jw := json.NewEncoder(w)
w.Header().Add("Content-Type", "application/json") w.Header().Add("Content-Type", "application/json")
jw.Encode(map[string][]microsub.Channel{ err = jw.Encode(map[string][]microsub.Channel{
"channels": channels, "channels": channels,
}) })
if err != nil {
http.Error(w, err.Error(), 500)
return
}
} else if action == "timeline" { } else if action == "timeline" {
timeline := h.Backend.TimelineGet(values.Get("before"), values.Get("after"), values.Get("channel")) timeline, err := h.Backend.TimelineGet(values.Get("before"), values.Get("after"), values.Get("channel"))
if err != nil {
http.Error(w, err.Error(), 500)
return
}
jw := json.NewEncoder(w) jw := json.NewEncoder(w)
w.Header().Add("Content-Type", "application/json") w.Header().Add("Content-Type", "application/json")
jw.SetIndent("", " ") jw.SetIndent("", " ")
jw.Encode(timeline) err = jw.Encode(timeline)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
} else if action == "preview" { } else if action == "preview" {
timeline := h.Backend.PreviewURL(values.Get("url")) timeline, err := h.Backend.PreviewURL(values.Get("url"))
if err != nil {
http.Error(w, err.Error(), 500)
return
}
jw := json.NewEncoder(w) jw := json.NewEncoder(w)
jw.SetIndent("", " ") jw.SetIndent("", " ")
w.Header().Add("Content-Type", "application/json") w.Header().Add("Content-Type", "application/json")
jw.Encode(timeline) err = jw.Encode(timeline)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
} else if action == "follow" { } else if action == "follow" {
channel := values.Get("channel") channel := values.Get("channel")
following := h.Backend.FollowGetList(channel) following, err := h.Backend.FollowGetList(channel)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
jw := json.NewEncoder(w) jw := json.NewEncoder(w)
w.Header().Add("Content-Type", "application/json") w.Header().Add("Content-Type", "application/json")
jw.Encode(map[string][]microsub.Feed{ err = jw.Encode(map[string][]microsub.Feed{
"items": following, "items": following,
}) })
if err != nil {
http.Error(w, err.Error(), 500)
return
}
} else { } else {
log.Printf("unknown action %s\n", action) http.Error(w, fmt.Sprintf("unknown action %s\n", action), 500)
return
} }
return return
} else if r.Method == http.MethodPost { } else if r.Method == http.MethodPost {
@ -87,7 +120,11 @@ func (h *microsubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
method := values.Get("method") method := values.Get("method")
uid := values.Get("channel") uid := values.Get("channel")
if method == "delete" { if method == "delete" {
h.Backend.ChannelsDelete(uid) err := h.Backend.ChannelsDelete(uid)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
w.Header().Add("Content-Type", "application/json") w.Header().Add("Content-Type", "application/json")
fmt.Fprintln(w, "[]") fmt.Fprintln(w, "[]")
h.Backend.(Debug).Debug() h.Backend.(Debug).Debug()
@ -96,37 +133,73 @@ func (h *microsubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
jw := json.NewEncoder(w) jw := json.NewEncoder(w)
if uid == "" { if uid == "" {
channel := h.Backend.ChannelsCreate(name) channel, err := h.Backend.ChannelsCreate(name)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
w.Header().Add("Content-Type", "application/json") w.Header().Add("Content-Type", "application/json")
jw.Encode(channel) err = jw.Encode(channel)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
} else { } else {
channel := h.Backend.ChannelsUpdate(uid, name) channel, err := h.Backend.ChannelsUpdate(uid, name)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
w.Header().Add("Content-Type", "application/json") w.Header().Add("Content-Type", "application/json")
jw.Encode(channel) err = jw.Encode(channel)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
} }
h.Backend.(Debug).Debug() h.Backend.(Debug).Debug()
} else if action == "follow" { } else if action == "follow" {
uid := values.Get("channel") uid := values.Get("channel")
url := values.Get("url") url := values.Get("url")
h.HubIncomingBackend.CreateFeed(url, uid) h.HubIncomingBackend.CreateFeed(url, uid)
feed := h.Backend.FollowURL(uid, url) feed, err := h.Backend.FollowURL(uid, url)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
w.Header().Add("Content-Type", "application/json") w.Header().Add("Content-Type", "application/json")
jw := json.NewEncoder(w) jw := json.NewEncoder(w)
jw.Encode(feed) err = jw.Encode(feed)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
} else if action == "unfollow" { } else if action == "unfollow" {
uid := values.Get("channel") uid := values.Get("channel")
url := values.Get("url") url := values.Get("url")
h.Backend.UnfollowURL(uid, url) err := h.Backend.UnfollowURL(uid, url)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
w.Header().Add("Content-Type", "application/json") w.Header().Add("Content-Type", "application/json")
fmt.Fprintln(w, "[]") fmt.Fprintln(w, "[]")
} else if action == "search" { } else if action == "search" {
query := values.Get("query") query := values.Get("query")
feeds := h.Backend.Search(query) feeds, err := h.Backend.Search(query)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
jw := json.NewEncoder(w) jw := json.NewEncoder(w)
w.Header().Add("Content-Type", "application/json") w.Header().Add("Content-Type", "application/json")
jw.Encode(map[string][]microsub.Feed{ err = jw.Encode(map[string][]microsub.Feed{
"results": feeds, "results": feeds,
}) })
if err != nil {
http.Error(w, err.Error(), 500)
return
}
} else if action == "timeline" || r.PostForm.Get("action") == "timeline" { } else if action == "timeline" || r.PostForm.Get("action") == "timeline" {
method := values.Get("method") method := values.Get("method")
@ -134,9 +207,17 @@ func (h *microsubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
values = r.Form values = r.Form
channel := values.Get("channel") channel := values.Get("channel")
if uids, e := values["entry"]; e { if uids, e := values["entry"]; e {
h.Backend.MarkRead(channel, uids) err := h.Backend.MarkRead(channel, uids)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
} else if uids, e := values["entry[]"]; e { } else if uids, e := values["entry[]"]; e {
h.Backend.MarkRead(channel, uids) err := h.Backend.MarkRead(channel, uids)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
} else { } else {
uids := []string{} uids := []string{}
for k, v := range values { for k, v := range values {
@ -144,15 +225,20 @@ func (h *microsubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
uids = append(uids, v...) uids = append(uids, v...)
} }
} }
h.Backend.MarkRead(channel, uids) err := h.Backend.MarkRead(channel, uids)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
} }
} else { } else {
log.Printf("unknown method in timeline %s\n", method) http.Error(w, fmt.Sprintf("unknown method in timeline %s\n", method), 500)
return
} }
w.Header().Add("Content-Type", "application/json") w.Header().Add("Content-Type", "application/json")
fmt.Fprintln(w, "[]") fmt.Fprintln(w, "[]")
} else { } else {
log.Printf("unknown action %s\n", action) http.Error(w, fmt.Sprintf("unknown action %s\n", action), 500)
} }
return return

View File

@ -18,7 +18,7 @@
package main package main
import ( import (
"github.com/pstuifzand/ekster/microsub" "github.com/pstuifzand/ekster/pkg/microsub"
) )
// NullBackend is the simplest possible backend // NullBackend is the simplest possible backend
@ -26,64 +26,67 @@ type NullBackend struct {
} }
// ChannelsGetList gets no channels // ChannelsGetList gets no channels
func (b *NullBackend) ChannelsGetList() []microsub.Channel { func (b *NullBackend) ChannelsGetList() ([]microsub.Channel, error) {
return []microsub.Channel{ return []microsub.Channel{
microsub.Channel{UID: "0000", Name: "default", Unread: 0}, microsub.Channel{UID: "0000", Name: "default", Unread: 0},
microsub.Channel{UID: "0001", Name: "notifications", Unread: 0}, microsub.Channel{UID: "0001", Name: "notifications", Unread: 0},
microsub.Channel{UID: "1000", Name: "Friends", Unread: 0}, microsub.Channel{UID: "1000", Name: "Friends", Unread: 0},
microsub.Channel{UID: "1001", Name: "Family", Unread: 0}, microsub.Channel{UID: "1001", Name: "Family", Unread: 0},
} }, nil
} }
// ChannelsCreate creates no channels // ChannelsCreate creates no channels
func (b *NullBackend) ChannelsCreate(name string) microsub.Channel { func (b *NullBackend) ChannelsCreate(name string) (microsub.Channel, error) {
return microsub.Channel{ return microsub.Channel{
UID: "1234", UID: "1234",
Name: name, Name: name,
} }, nil
} }
// ChannelsUpdate updates no channels // ChannelsUpdate updates no channels
func (b *NullBackend) ChannelsUpdate(uid, name string) microsub.Channel { func (b *NullBackend) ChannelsUpdate(uid, name string) (microsub.Channel, error) {
return microsub.Channel{ return microsub.Channel{
UID: uid, UID: uid,
Name: name, Name: name,
} }, nil
} }
// ChannelsDelete delets no channels // ChannelsDelete delets no channels
func (b *NullBackend) ChannelsDelete(uid string) { func (b *NullBackend) ChannelsDelete(uid string) error {
return nil
} }
// TimelineGet gets no timeline // TimelineGet gets no timeline
func (b *NullBackend) TimelineGet(before, after, channel string) microsub.Timeline { func (b *NullBackend) TimelineGet(before, after, channel string) (microsub.Timeline, error) {
return microsub.Timeline{ return microsub.Timeline{
Paging: microsub.Pagination{}, Paging: microsub.Pagination{},
Items: []microsub.Item{}, Items: []microsub.Item{},
} }, nil
} }
func (b *NullBackend) FollowGetList(uid string) []microsub.Feed { func (b *NullBackend) FollowGetList(uid string) ([]microsub.Feed, error) {
return []microsub.Feed{} return []microsub.Feed{}, nil
} }
func (b *NullBackend) FollowURL(uid string, url string) microsub.Feed { func (b *NullBackend) FollowURL(uid string, url string) (microsub.Feed, error) {
return microsub.Feed{Type: "feed", URL: url} return microsub.Feed{Type: "feed", URL: url}, nil
} }
func (b *NullBackend) UnfollowURL(uid string, url string) { func (b *NullBackend) UnfollowURL(uid string, url string) error {
return nil
} }
func (b *NullBackend) Search(query string) []microsub.Feed { func (b *NullBackend) Search(query string) ([]microsub.Feed, error) {
return []microsub.Feed{} return []microsub.Feed{}, nil
} }
func (b *NullBackend) PreviewURL(url string) microsub.Timeline { func (b *NullBackend) PreviewURL(url string) (microsub.Timeline, error) {
return microsub.Timeline{ return microsub.Timeline{
Paging: microsub.Pagination{}, Paging: microsub.Pagination{},
Items: []microsub.Item{}, Items: []microsub.Item{},
} }, nil
} }
func (b *NullBackend) MarkRead(channel string, uids []string) { func (b *NullBackend) MarkRead(channel string, uids []string) error {
return nil
} }

View File

@ -3,12 +3,11 @@ package client
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"log"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
"github.com/pstuifzand/ekster/microsub" "github.com/pstuifzand/ekster/pkg/microsub"
) )
type Client struct { type Client struct {
@ -80,12 +79,11 @@ func (c *Client) microsubPostFormRequest(action string, args map[string]string,
return client.Do(req) return client.Do(req)
} }
func (c *Client) ChannelsGetList() []microsub.Channel { 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)
if err != nil { if err != nil {
log.Println(err) return []microsub.Channel{}, err
return []microsub.Channel{}
} }
defer res.Body.Close() defer res.Body.Close()
@ -95,52 +93,56 @@ func (c *Client) ChannelsGetList() []microsub.Channel {
dec := json.NewDecoder(res.Body) dec := json.NewDecoder(res.Body)
var channels channelsResponse var channels channelsResponse
dec.Decode(&channels) err = dec.Decode(&channels)
return channels.Channels if err != nil {
return channels.Channels, err
}
return channels.Channels, nil
} }
func (c *Client) TimelineGet(before, after, channel string) microsub.Timeline { 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
args["before"] = before args["before"] = before
args["channel"] = channel args["channel"] = channel
res, err := c.microsubGetRequest("timeline", args) res, err := c.microsubGetRequest("timeline", args)
if err != nil { if err != nil {
log.Println(err) return microsub.Timeline{}, err
return microsub.Timeline{}
} }
defer res.Body.Close() defer res.Body.Close()
dec := json.NewDecoder(res.Body) dec := json.NewDecoder(res.Body)
var timeline microsub.Timeline var timeline microsub.Timeline
err = dec.Decode(&timeline) err = dec.Decode(&timeline)
if err != nil { if err != nil {
log.Fatal(err) return microsub.Timeline{}, err
} }
return timeline return timeline, nil
} }
func (c *Client) PreviewURL(url string) microsub.Timeline { 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
res, err := c.microsubGetRequest("preview", args) res, err := c.microsubGetRequest("preview", args)
if err != nil { if err != nil {
log.Println(err) return microsub.Timeline{}, err
return microsub.Timeline{}
} }
defer res.Body.Close() defer res.Body.Close()
dec := json.NewDecoder(res.Body) dec := json.NewDecoder(res.Body)
var timeline microsub.Timeline var timeline microsub.Timeline
dec.Decode(&timeline) err = dec.Decode(&timeline)
return timeline if err != nil {
return microsub.Timeline{}, err
}
return timeline, nil
} }
func (c *Client) FollowGetList(channel string) []microsub.Feed { func (c *Client) FollowGetList(channel string) ([]microsub.Feed, error) {
args := make(map[string]string) args := make(map[string]string)
args["channel"] = channel args["channel"] = channel
res, err := c.microsubGetRequest("follow", args) res, err := c.microsubGetRequest("follow", args)
if err != nil { if err != nil {
log.Println(err) return []microsub.Feed{}, nil
return []microsub.Feed{}
} }
defer res.Body.Close() defer res.Body.Close()
dec := json.NewDecoder(res.Body) dec := json.NewDecoder(res.Body)
@ -148,88 +150,96 @@ func (c *Client) FollowGetList(channel string) []microsub.Feed {
Items []microsub.Feed `json:"items"` Items []microsub.Feed `json:"items"`
} }
var response followResponse var response followResponse
dec.Decode(&response) err = dec.Decode(&response)
return response.Items if err != nil {
return []microsub.Feed{}, nil
}
return response.Items, nil
} }
func (c *Client) ChannelsCreate(name string) microsub.Channel { func (c *Client) ChannelsCreate(name string) (microsub.Channel, error) {
args := make(map[string]string) args := make(map[string]string)
args["name"] = name args["name"] = name
res, err := c.microsubPostRequest("channels", args) res, err := c.microsubPostRequest("channels", args)
if err != nil { if err != nil {
log.Println(err) return microsub.Channel{}, nil
return microsub.Channel{}
} }
defer res.Body.Close() defer res.Body.Close()
var channel microsub.Channel var channel microsub.Channel
dec := json.NewDecoder(res.Body) dec := json.NewDecoder(res.Body)
dec.Decode(&channel) err = dec.Decode(&channel)
return channel if err != nil {
return microsub.Channel{}, nil
}
return channel, nil
} }
func (c *Client) ChannelsUpdate(uid, name string) microsub.Channel { func (c *Client) ChannelsUpdate(uid, name string) (microsub.Channel, error) {
args := make(map[string]string) args := make(map[string]string)
args["name"] = name args["name"] = name
args["uid"] = uid args["uid"] = uid
res, err := c.microsubPostRequest("channels", args) res, err := c.microsubPostRequest("channels", args)
if err != nil { if err != nil {
log.Println(err) return microsub.Channel{}, err
return microsub.Channel{}
} }
defer res.Body.Close() defer res.Body.Close()
var channel microsub.Channel var channel microsub.Channel
dec := json.NewDecoder(res.Body) dec := json.NewDecoder(res.Body)
dec.Decode(&channel) err = dec.Decode(&channel)
return channel if err != nil {
return microsub.Channel{}, err
}
return channel, nil
} }
func (c *Client) ChannelsDelete(uid string) { func (c *Client) ChannelsDelete(uid string) error {
args := make(map[string]string) args := make(map[string]string)
args["channel"] = uid args["channel"] = uid
args["method"] = "delete" args["method"] = "delete"
res, err := c.microsubPostRequest("channels", args) res, err := c.microsubPostRequest("channels", args)
if err != nil { if err != nil {
log.Println(err) return err
return
} }
res.Body.Close() res.Body.Close()
return nil
} }
func (c *Client) FollowURL(channel, url string) microsub.Feed { func (c *Client) FollowURL(channel, url string) (microsub.Feed, error) {
args := make(map[string]string) args := make(map[string]string)
args["channel"] = channel args["channel"] = channel
args["url"] = url args["url"] = url
res, err := c.microsubPostRequest("follow", args) res, err := c.microsubPostRequest("follow", args)
if err != nil { if err != nil {
log.Println(err) return microsub.Feed{}, err
return microsub.Feed{}
} }
defer res.Body.Close() defer res.Body.Close()
var feed microsub.Feed var feed microsub.Feed
dec := json.NewDecoder(res.Body) dec := json.NewDecoder(res.Body)
dec.Decode(&feed) err = dec.Decode(&feed)
return feed if err != nil {
return microsub.Feed{}, err
}
return feed, nil
} }
func (c *Client) UnfollowURL(channel, url string) { func (c *Client) UnfollowURL(channel, url string) error {
args := make(map[string]string) args := make(map[string]string)
args["channel"] = channel args["channel"] = channel
args["url"] = url args["url"] = url
res, err := c.microsubPostRequest("unfollow", args) res, err := c.microsubPostRequest("unfollow", args)
if err != nil { if err != nil {
log.Println(err) return err
return
} }
res.Body.Close() res.Body.Close()
return nil
} }
func (c *Client) Search(query string) []microsub.Feed { func (c *Client) Search(query string) ([]microsub.Feed, error) {
args := make(map[string]string) args := make(map[string]string)
args["query"] = query args["query"] = query
res, err := c.microsubPostRequest("search", args) res, err := c.microsubPostRequest("search", args)
if err != nil { if err != nil {
log.Println(err) return []microsub.Feed{}, err
return []microsub.Feed{}
} }
type searchResponse struct { type searchResponse struct {
Results []microsub.Feed `json:"results"` Results []microsub.Feed `json:"results"`
@ -237,11 +247,14 @@ func (c *Client) Search(query string) []microsub.Feed {
defer res.Body.Close() defer res.Body.Close()
var response searchResponse var response searchResponse
dec := json.NewDecoder(res.Body) dec := json.NewDecoder(res.Body)
dec.Decode(&response) err = dec.Decode(&response)
return response.Results if err != nil {
return []microsub.Feed{}, err
}
return response.Results, nil
} }
func (c *Client) MarkRead(channel string, uids []string) { func (c *Client) MarkRead(channel string, uids []string) error {
args := make(map[string]string) args := make(map[string]string)
args["channel"] = channel args["channel"] = channel
@ -251,7 +264,9 @@ func (c *Client) MarkRead(channel string, uids []string) {
} }
res, err := c.microsubPostFormRequest("mark_read", args, data) res, err := c.microsubPostFormRequest("mark_read", args, data)
if err == nil { if err != nil {
defer res.Body.Close() return err
} }
res.Body.Close()
return nil
} }

View File

@ -102,20 +102,20 @@ type Feed struct {
// Microsub is the main protocol that should be implemented by a backend // Microsub is the main protocol that should be implemented by a backend
type Microsub interface { type Microsub interface {
ChannelsGetList() []Channel ChannelsGetList() ([]Channel, error)
ChannelsCreate(name string) Channel ChannelsCreate(name string) (Channel, error)
ChannelsUpdate(uid, name string) Channel ChannelsUpdate(uid, name string) (Channel, error)
ChannelsDelete(uid string) ChannelsDelete(uid string) error
TimelineGet(before, after, channel string) Timeline TimelineGet(before, after, channel string) (Timeline, error)
MarkRead(channel string, entry []string) MarkRead(channel string, entry []string) error
FollowGetList(uid string) []Feed FollowGetList(uid string) ([]Feed, error)
FollowURL(uid string, url string) Feed FollowURL(uid string, url string) (Feed, error)
UnfollowURL(uid string, url string) UnfollowURL(uid string, url string) error
Search(query string) []Feed Search(query string) ([]Feed, error)
PreviewURL(url string) Timeline PreviewURL(url string) (Timeline, error)
} }

View File

@ -0,0 +1,15 @@
package microsub
import (
"encoding/json"
"fmt"
"testing"
"github.com/pstuifzand/ekster/microsub"
)
func TestJson(t *testing.T) {
item := microsub.Item{Type: "entry"}
result, err := json.Marshal(item)
fmt.Println(string(result), err)
}