Преглед изворни кода

Merge pull request #29 from writeas/T604

fetch authenticated users posts
pull/33/head
Matt Baer пре 5 година
committed by GitHub
родитељ
комит
440e210afa
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
6 измењених фајлова са 271 додато и 17 уклоњено
  1. +16
    -0
      api/api.go
  2. +92
    -0
      api/posts.go
  3. +103
    -0
      api/posts_test.go
  4. +4
    -3
      cmd/writeas/main.go
  5. +52
    -14
      commands/commands.go
  6. +4
    -0
      go.sum

+ 16
- 0
api/api.go Прегледај датотеку

@@ -69,6 +69,22 @@ func DoFetch(friendlyID, ua string, tor bool) error {
return nil
}

// DoFetchPosts retrieves all remote posts for the
// authenticated user
func DoFetchPosts(c *cli.Context) ([]writeas.Post, error) {
cl, err := NewClient(c, true)
if err != nil {
return nil, err
}

posts, err := cl.GetUserPosts()
if err != nil {
return nil, err
}

return *posts, nil
}

// DoPost creates a Write.as post, returning an error if it was
// unsuccessful.
func DoPost(c *cli.Context, post []byte, font string, encrypt, tor, code bool) (*writeas.Post, error) {


+ 92
- 0
api/posts.go Прегледај датотеку

@@ -8,6 +8,7 @@ import (
"os"
"path/filepath"
"strings"
"time"

"github.com/writeas/writeas-cli/config"
"github.com/writeas/writeas-cli/fileutils"
@@ -27,6 +28,19 @@ type Post struct {
EditToken string
}

// RemotePost holds addition information about published
// posts
type RemotePost struct {
Post
Title,
Excerpt,
Slug,
Collection,
EditToken string
Synced bool
Updated time.Time
}

func AddPost(c *cli.Context, id, token string) error {
f, err := os.OpenFile(filepath.Join(config.UserDataDir(c.App.ExtraInfo()["configDir"]), postsFile), os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
if err != nil {
@@ -80,6 +94,84 @@ func GetPosts(c *cli.Context) *[]Post {
return &posts
}

func GetUserPosts(c *cli.Context) ([]RemotePost, error) {
waposts, err := DoFetchPosts(c)
if err != nil {
return nil, err
}

if len(waposts) == 0 {
return nil, nil
}

posts := []RemotePost{}
for _, p := range waposts {
post := RemotePost{
Post: Post{
ID: p.ID,
EditToken: p.Token,
},
Title: p.Title,
Excerpt: getExcerpt(p.Content),
Slug: p.Slug,
Synced: p.Slug != "",
Updated: p.Updated,
}
if p.Collection != nil {
post.Collection = p.Collection.Alias
}
posts = append(posts, post)
}

return posts, nil
}

// getExcerpt takes in a content string and returns
// a concatenated version. limited to no more than
// two lines of 80 chars each. delimited by '...'
func getExcerpt(input string) string {
length := len(input)

if length <= 80 {
return input
} else if length < 160 {
ln1, idx := trimToLength(input, 80)
if idx == -1 {
idx = 80
}
ln2, _ := trimToLength(input[idx:], 80)
return ln1 + "\n" + ln2
} else {
excerpt := input[:158]
ln1, idx := trimToLength(excerpt, 80)
if idx == -1 {
idx = 80
}
ln2, _ := trimToLength(excerpt[idx:], 80)
return ln1 + "\n" + ln2 + "..."
}
}

func trimToLength(in string, l int) (string, int) {
c := []rune(in)
spaceIdx := -1
length := len(c)
if length <= l {
return in, spaceIdx
}

for i := l; i > 0; i-- {
if c[i] == ' ' {
spaceIdx = i
break
}
}
if spaceIdx > -1 {
c = c[:spaceIdx]
}
return string(c), spaceIdx
}

func ComposeNewPost() (string, *[]byte) {
f, err := fileutils.TempFile(os.TempDir(), "WApost", "txt")
if err != nil {


+ 103
- 0
api/posts_test.go Прегледај датотеку

@@ -0,0 +1,103 @@
package api

import "testing"

func TestTrimToLength(t *testing.T) {
tt := []struct {
Name string
Data string
Length int
ResultData string
ResultIDX int
}{
{
"English, longer than trim length",
"This is a string, let's truncate it.",
12,
"This is a",
9,
}, {
"English, equal to length",
"Some other string.",
18,
"Some other string.",
-1,
}, {
"English, shorter than trim length",
"I'm short!",
20,
"I'm short!",
-1,
}, {
"Multi-byte, longer than trim length",
"這是一個較長的廣東話。 有許多特性可以確保足夠長的輸出。",
14,
"這是一個較長的廣東話。",
11,
}, {
"Multi-byte, equal to length",
"這是一個簡短的廣東話。",
11,
"這是一個簡短的廣東話。",
-1,
}, {
"Multi-byte, shorter than trim length",
"我也很矮! 有空間。",
20,
"我也很矮! 有空間。",
-1,
},
}

for _, tc := range tt {
t.Run(tc.Name, func(t *testing.T) {
out, idx := trimToLength(tc.Data, tc.Length)
if out != tc.ResultData {
t.Errorf("Incorrect output, expecting \"%s\" but got \"%s\"", tc.ResultData, out)
}

if idx != tc.ResultIDX {
t.Errorf("Incorrect last index, expected \"%d\" but got \"%d\"", tc.ResultIDX, idx)
}
})
}
}

func TestGetExcerpt(t *testing.T) {
tt := []struct {
Name string
Data string
Result string
}{
{
"Shorter than one line",
"This is much less than 80 chars",
"This is much less than 80 chars",
}, {
"Exact length, one line",
"This will be only 80 chars. Maybe all the way to column 88, that will do it. ---",
"This will be only 80 chars. Maybe all the way to column 88, that will do it. ---",
}, {
"Shorter than two lines",
"This will be more than one line but shorter than two. It should break at the 80th or less character. Let's check it out.",
"This will be more than one line but shorter than two. It should break at the\n 80th or less character. Let's check it out.",
}, {
"Exact length, two lines",
"This should be the exact length for two lines. There should ideally be no trailing periods to indicate further text. However trimToLength breaks on word bounds.",
"This should be the exact length for two lines. There should ideally be no\n trailing periods to indicate further text. However trimToLength breaks on word...",
}, {
"Longer than two lines",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque volutpat sagittis aliquet. Ut eu rutrum nisl. Proin molestie ante in dui vulputate dictum. Proin ac bibendum eros. Nulla porta congue tellus, sed vehicula sem bibendum eu. Donec vehicula erat viverra fermentum mattis. Integer volutpat.",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque volutpat\n sagittis aliquet. Ut eu rutrum nisl. Proin molestie ante in dui vulputate...",
},
}

for _, tc := range tt {
t.Run(tc.Name, func(t *testing.T) {
out := getExcerpt(tc.Data)
if out != tc.Result {
t.Errorf("Output does not match:\nexpected \"%s\"\nbut got \"%s\"", tc.Result, out)
}
})
}
}

+ 4
- 3
cmd/writeas/main.go Прегледај датотеку

@@ -152,9 +152,10 @@ func main() {
Action: commands.CmdAdd,
},
{
Name: "list",
Usage: "List local posts",
Action: commands.CmdList,
Name: "posts",
Usage: "List all of your posts",
Description: "This will list only local posts when not currently authenticated. To list remote posts as well, first run: writeas auth <username>.",
Action: commands.CmdListPosts,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "id",


+ 52
- 14
commands/commands.go Прегледај датотеку

@@ -5,6 +5,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"text/tabwriter"

"github.com/howeyc/gopass"
"github.com/writeas/writeas-cli/api"
@@ -183,32 +184,69 @@ func CmdAdd(c *cli.Context) error {
return err
}

func CmdList(c *cli.Context) error {
func CmdListPosts(c *cli.Context) error {
urls := c.Bool("url")
ids := c.Bool("id")

var p api.Post
posts := api.GetPosts(c)
tw := tabwriter.NewWriter(os.Stdout, 10, 0, 2, ' ', tabwriter.TabIndent)
numPosts := len(*posts)
if ids || !urls && numPosts != 0 {
fmt.Fprintf(tw, "Local\t%s\t%s\t\n", "ID", "Token")
} else if numPosts != 0 {
fmt.Fprintf(tw, "Local\t%s\t%s\t\n", "URL", "Token")
} else {
fmt.Fprintf(tw, "No local posts found\n")
}
for i := range *posts {
p = (*posts)[len(*posts)-1-i]
p = (*posts)[numPosts-1-i]
if ids || !urls {
fmt.Printf("%s ", p.ID)
fmt.Fprintf(tw, "unsynced\t%s\t%s\t\n", p.ID, p.EditToken)
} else {
fmt.Fprintf(tw, "unsynced\t%s\t%s\t\n", getPostURL(c, p.ID), p.EditToken)
}
}
u, _ := config.LoadUser(config.UserDataDir(c.App.ExtraInfo()["configDir"]))
if u != nil {
remotePosts, err := api.GetUserPosts(c)
if err != nil {
fmt.Println(err)
}

if len(remotePosts) > 0 {
identifier := "URL"
if ids || !urls {
identifier = "ID"
}
fmt.Fprintf(tw, "\nAccount\t%s\t%s\t\n", identifier, "Title")
}
if urls {
base := config.WriteasBaseURL
if config.IsDev() {
base = config.DevBaseURL
for _, p := range remotePosts {
identifier := getPostURL(c, p.ID)
if ids || !urls {
identifier = p.ID
}
ext := ""
// Output URL in requested format
if c.Bool("md") {
ext = ".md"
synced := "unsynced"
if p.Synced {
synced = "synced"
}
fmt.Printf("%s/%s%s ", base, p.ID, ext)
fmt.Fprintf(tw, "%s\t%s\t%s\t\n", synced, identifier, p.Title)
}
fmt.Print("\n")
}
return nil
return tw.Flush()
}

func getPostURL(c *cli.Context, slug string) string {
base := config.WriteasBaseURL
if config.IsDev() {
base = config.DevBaseURL
}
ext := ""
// Output URL in requested format
if c.Bool("md") {
ext = ".md"
}
return fmt.Sprintf("%s/%s%s", base, slug, ext)
}

func CmdAuth(c *cli.Context) error {


+ 4
- 0
go.sum Прегледај датотеку

@@ -4,7 +4,9 @@ github.com/atotto/clipboard v0.1.1 h1:WSoEbAS70E5gw8FbiqFlp69MGsB6dUb4l+0AGGLiVG
github.com/atotto/clipboard v0.1.1/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do=
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@@ -42,12 +44,14 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181217023233-e147a9138326 h1:iCzOf0xz39Tstp+Tu/WwyGjUXCk34QhQORRxBeXXTA4=
golang.org/x/net v0.0.0-20181217023233-e147a9138326/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06 h1:0oC8rFnE+74kEmuHZ46F6KHsMr5Gx2gUQPuNz28iQZM=
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=


Loading…
Откажи
Сачувај