Add first integration test
This commit is contained in:
parent
71bf7e4358
commit
142e8b3e0c
14 changed files with 891 additions and 12 deletions
|
@ -1,5 +1,9 @@
|
|||
notifications:
|
||||
email: false
|
||||
services:
|
||||
- postgresql
|
||||
addons:
|
||||
postgresql: "9.4"
|
||||
language: go
|
||||
go:
|
||||
- 1.9
|
||||
|
@ -7,4 +11,5 @@ before_install:
|
|||
- npm install -g jshint
|
||||
script:
|
||||
- jshint server/static/js/app.js
|
||||
- go test -cover -race ./...
|
||||
- make test
|
||||
- make integration-test
|
||||
|
|
8
Gopkg.lock
generated
8
Gopkg.lock
generated
|
@ -37,6 +37,12 @@
|
|||
packages = [".","hstore","oid"]
|
||||
revision = "8c6ee72f3e6bcb1542298dd5f76cb74af9742cec"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/miniflux/miniflux-go"
|
||||
packages = ["."]
|
||||
revision = "a2caa9187ebe4378f36c8e680825586a890154c9"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/tdewolff/minify"
|
||||
packages = [".","css","js"]
|
||||
|
@ -94,6 +100,6 @@
|
|||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "62e971001374c00b358ba709b9694393eec4a1a84d94a946b21251fa81d98af7"
|
||||
inputs-digest = "ade513f4a86a1f49ce3508aa55f40cf2b1f172d6f3679649e48de67a82715431"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
|
19
Makefile
19
Makefile
|
@ -1,8 +1,9 @@
|
|||
APP = miniflux
|
||||
VERSION = $(shell git rev-parse --short HEAD)
|
||||
BUILD_DATE = `date +%FT%T%z`
|
||||
DB_URL = postgres://postgres:postgres@localhost/miniflux_test?sslmode=disable
|
||||
|
||||
.PHONY: build-linux build-darwin build run clean test
|
||||
.PHONY: build-linux build-darwin build run clean test integration-test clean-integration-test
|
||||
|
||||
build-linux:
|
||||
@ go generate
|
||||
|
@ -23,3 +24,19 @@ clean:
|
|||
|
||||
test:
|
||||
go test -cover -race ./...
|
||||
|
||||
integration-test:
|
||||
psql -U postgres -c 'drop database if exists miniflux_test;'
|
||||
psql -U postgres -c 'create database miniflux_test;'
|
||||
DATABASE_URL=$(DB_URL) go run main.go -migrate
|
||||
DATABASE_URL=$(DB_URL) ADMIN_USERNAME=admin ADMIN_PASSWORD=test123 go run main.go -create-admin
|
||||
go build -o miniflux-test main.go
|
||||
DATABASE_URL=$(DB_URL) ./miniflux-test >/tmp/miniflux.log 2>&1 & echo "$$!" > "/tmp/miniflux.pid"
|
||||
while ! echo exit | nc localhost 8080; do sleep 1; done >/dev/null
|
||||
go test -v -tags=integration || cat /tmp/miniflux.log
|
||||
|
||||
clean-integration-test:
|
||||
@ kill -9 `cat /tmp/miniflux.pid`
|
||||
@ rm -f /tmp/miniflux.pid /tmp/miniflux.log
|
||||
@ rm miniflux-test
|
||||
@ psql -U postgres -c 'drop database if exists miniflux_test;'
|
||||
|
|
101
integration_test.go
Normal file
101
integration_test.go
Normal file
|
@ -0,0 +1,101 @@
|
|||
// Copyright 2017 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build integration
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/miniflux/miniflux-go"
|
||||
)
|
||||
|
||||
const (
|
||||
testBaseURL = "http://127.0.0.1:8080"
|
||||
testUsername = "admin"
|
||||
testPassword = "test123"
|
||||
)
|
||||
|
||||
func TestGetUsers(t *testing.T) {
|
||||
client := miniflux.NewClient(testBaseURL, testUsername, testPassword)
|
||||
users, err := client.Users()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(users) == 0 {
|
||||
t.Fatal("The list of users is empty")
|
||||
}
|
||||
|
||||
if users[0].ID == 0 {
|
||||
t.Fatalf(`Invalid userID, got %v`, users[0].ID)
|
||||
}
|
||||
|
||||
if users[0].Username != testUsername {
|
||||
t.Fatalf(`Invalid username, got %v`, users[0].Username)
|
||||
}
|
||||
|
||||
if users[0].Password != "" {
|
||||
t.Fatalf(`Invalid password, got %v`, users[0].Password)
|
||||
}
|
||||
|
||||
if users[0].Language != "en_US" {
|
||||
t.Fatalf(`Invalid language, got %v`, users[0].Language)
|
||||
}
|
||||
|
||||
if users[0].Theme != "default" {
|
||||
t.Fatalf(`Invalid theme, got %v`, users[0].Theme)
|
||||
}
|
||||
|
||||
if users[0].Timezone != "UTC" {
|
||||
t.Fatalf(`Invalid timezone, got %v`, users[0].Timezone)
|
||||
}
|
||||
|
||||
if !users[0].IsAdmin {
|
||||
t.Fatalf(`Invalid role, got %v`, users[0].IsAdmin)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateStandardUser(t *testing.T) {
|
||||
client := miniflux.NewClient(testBaseURL, testUsername, testPassword)
|
||||
user, err := client.CreateUser("test", "test123", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
if user.ID == 0 {
|
||||
t.Fatalf(`Invalid userID, got %v`, user.ID)
|
||||
}
|
||||
|
||||
if user.Username != "test" {
|
||||
t.Fatalf(`Invalid username, got %v`, user.Username)
|
||||
}
|
||||
|
||||
if user.Password != "" {
|
||||
t.Fatalf(`Invalid password, got %v`, user.Password)
|
||||
}
|
||||
|
||||
if user.Language != "en_US" {
|
||||
t.Fatalf(`Invalid language, got %v`, user.Language)
|
||||
}
|
||||
|
||||
if user.Theme != "default" {
|
||||
t.Fatalf(`Invalid theme, got %v`, user.Theme)
|
||||
}
|
||||
|
||||
if user.Timezone != "UTC" {
|
||||
t.Fatalf(`Invalid timezone, got %v`, user.Timezone)
|
||||
}
|
||||
|
||||
if user.IsAdmin {
|
||||
t.Fatalf(`Invalid role, got %v`, user.IsAdmin)
|
||||
}
|
||||
|
||||
if user.LastLoginAt != nil {
|
||||
t.Fatalf(`Invalid last login date, got %v`, user.LastLoginAt)
|
||||
}
|
||||
}
|
10
main.go
10
main.go
|
@ -116,8 +116,16 @@ func main() {
|
|||
}
|
||||
|
||||
if *flagCreateAdmin {
|
||||
user := &model.User{IsAdmin: true}
|
||||
user := &model.User{
|
||||
Username: os.Getenv("ADMIN_USERNAME"),
|
||||
Password: os.Getenv("ADMIN_PASSWORD"),
|
||||
IsAdmin: true,
|
||||
}
|
||||
|
||||
if user.Username == "" || user.Password == "" {
|
||||
user.Username, user.Password = askCredentials()
|
||||
}
|
||||
|
||||
if err := user.ValidateUserCreation(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
|
|
|
@ -14,11 +14,11 @@ type User struct {
|
|||
ID int64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password,omitempty"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
Theme string `json:"theme"`
|
||||
Language string `json:"language"`
|
||||
Timezone string `json:"timezone"`
|
||||
LastLoginAt *time.Time `json:"last_login_at"`
|
||||
IsAdmin bool `json:"is_admin,omitempty"`
|
||||
Theme string `json:"theme,omitempty"`
|
||||
Language string `json:"language,omitempty"`
|
||||
Timezone string `json:"timezone,omitempty"`
|
||||
LastLoginAt *time.Time `json:"last_login_at,omitempty"`
|
||||
Extra map[string]string `json:"-"`
|
||||
}
|
||||
|
||||
|
|
|
@ -64,8 +64,18 @@ func (s *Storage) CreateUser(user *model.User) (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
query := "INSERT INTO users (username, password, is_admin, extra) VALUES ($1, $2, $3, $4) RETURNING id"
|
||||
err = s.db.QueryRow(query, strings.ToLower(user.Username), password, user.IsAdmin, extra).Scan(&user.ID)
|
||||
query := `INSERT INTO users
|
||||
(username, password, is_admin, extra)
|
||||
VALUES
|
||||
($1, $2, $3, $4)
|
||||
RETURNING id, language, theme, timezone`
|
||||
|
||||
err = s.db.QueryRow(query, strings.ToLower(user.Username), password, user.IsAdmin, extra).Scan(
|
||||
&user.ID,
|
||||
&user.Language,
|
||||
&user.Theme,
|
||||
&user.Timezone,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create user: %v", err)
|
||||
}
|
||||
|
|
9
vendor/github.com/miniflux/miniflux-go/.travis.yml
generated
vendored
Normal file
9
vendor/github.com/miniflux/miniflux-go/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
notifications:
|
||||
email: false
|
||||
language: go
|
||||
go:
|
||||
- 1.9
|
||||
before_install:
|
||||
- go get -u github.com/golang/lint/golint
|
||||
script:
|
||||
- golint *.go
|
21
vendor/github.com/miniflux/miniflux-go/LICENSE
generated
vendored
Normal file
21
vendor/github.com/miniflux/miniflux-go/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Frédéric Guillot
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
50
vendor/github.com/miniflux/miniflux-go/README.md
generated
vendored
Normal file
50
vendor/github.com/miniflux/miniflux-go/README.md
generated
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
Go Library for Miniflux
|
||||
=======================
|
||||
[![Build Status](https://travis-ci.org/miniflux/miniflux-go.svg?branch=master)](https://travis-ci.org/miniflux/miniflux-go)
|
||||
[![GoDoc](https://godoc.org/github.com/miniflux/miniflux-go?status.svg)](https://godoc.org/github.com/miniflux/miniflux-go)
|
||||
|
||||
Client library for Miniflux REST API.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
- Miniflux >= 2.0.0
|
||||
- Go >= 1.9
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
```bash
|
||||
go get -u github.com/miniflux/miniflux-go
|
||||
```
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/miniflux/miniflux-go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
client := miniflux.NewClient("https://api.example.org", "admin", "secret")
|
||||
|
||||
// Fetch all feeds.
|
||||
feeds, err := userClient.Feeds()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println(feeds)
|
||||
}
|
||||
```
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
- Author: Frédéric Guillot
|
||||
- Distributed under MIT License
|
354
vendor/github.com/miniflux/miniflux-go/client.go
generated
vendored
Normal file
354
vendor/github.com/miniflux/miniflux-go/client.go
generated
vendored
Normal file
|
@ -0,0 +1,354 @@
|
|||
// Copyright 2017 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package miniflux
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Client represents a Miniflux client.
|
||||
type Client struct {
|
||||
request *request
|
||||
}
|
||||
|
||||
// Users returns all users.
|
||||
func (c *Client) Users() (Users, error) {
|
||||
body, err := c.request.Get("/v1/users")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer body.Close()
|
||||
|
||||
var users Users
|
||||
decoder := json.NewDecoder(body)
|
||||
if err := decoder.Decode(&users); err != nil {
|
||||
return nil, fmt.Errorf("miniflux: response error (%v)", err)
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
// User returns a single user.
|
||||
func (c *Client) User(userID int64) (*User, error) {
|
||||
body, err := c.request.Get(fmt.Sprintf("/v1/users/%d", userID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer body.Close()
|
||||
|
||||
var user User
|
||||
decoder := json.NewDecoder(body)
|
||||
if err := decoder.Decode(&user); err != nil {
|
||||
return nil, fmt.Errorf("miniflux: response error (%v)", err)
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// CreateUser creates a new user in the system.
|
||||
func (c *Client) CreateUser(username, password string, isAdmin bool) (*User, error) {
|
||||
body, err := c.request.Post("/v1/users", &User{Username: username, Password: password, IsAdmin: isAdmin})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer body.Close()
|
||||
|
||||
var user *User
|
||||
decoder := json.NewDecoder(body)
|
||||
if err := decoder.Decode(&user); err != nil {
|
||||
return nil, fmt.Errorf("miniflux: response error (%v)", err)
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// UpdateUser updates a user in the system.
|
||||
func (c *Client) UpdateUser(user *User) (*User, error) {
|
||||
body, err := c.request.Put(fmt.Sprintf("/v1/users/%d", user.ID), user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer body.Close()
|
||||
|
||||
var u *User
|
||||
decoder := json.NewDecoder(body)
|
||||
if err := decoder.Decode(&u); err != nil {
|
||||
return nil, fmt.Errorf("miniflux: response error (%v)", err)
|
||||
}
|
||||
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// DeleteUser removes a user from the system.
|
||||
func (c *Client) DeleteUser(userID int64) error {
|
||||
body, err := c.request.Delete(fmt.Sprintf("/v1/users/%d", userID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Discover try to find subscriptions from a website.
|
||||
func (c *Client) Discover(url string) (Subscriptions, error) {
|
||||
body, err := c.request.Post("/v1/discover", map[string]string{"url": url})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer body.Close()
|
||||
|
||||
var subscriptions Subscriptions
|
||||
decoder := json.NewDecoder(body)
|
||||
if err := decoder.Decode(&subscriptions); err != nil {
|
||||
return nil, fmt.Errorf("miniflux: response error (%v)", err)
|
||||
}
|
||||
|
||||
return subscriptions, nil
|
||||
}
|
||||
|
||||
// Categories gets the list of categories.
|
||||
func (c *Client) Categories() (Categories, error) {
|
||||
body, err := c.request.Get("/v1/categories")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer body.Close()
|
||||
|
||||
var categories Categories
|
||||
decoder := json.NewDecoder(body)
|
||||
if err := decoder.Decode(&categories); err != nil {
|
||||
return nil, fmt.Errorf("miniflux: response error (%v)", err)
|
||||
}
|
||||
|
||||
return categories, nil
|
||||
}
|
||||
|
||||
// CreateCategory creates a new category.
|
||||
func (c *Client) CreateCategory(title string) (*Category, error) {
|
||||
body, err := c.request.Post("/v1/categories", map[string]interface{}{
|
||||
"title": title,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer body.Close()
|
||||
|
||||
var category *Category
|
||||
decoder := json.NewDecoder(body)
|
||||
if err := decoder.Decode(&category); err != nil {
|
||||
return nil, fmt.Errorf("miniflux: response error (%v)", err)
|
||||
}
|
||||
|
||||
return category, nil
|
||||
}
|
||||
|
||||
// UpdateCategory updates a category.
|
||||
func (c *Client) UpdateCategory(categoryID int64, title string) (*Category, error) {
|
||||
body, err := c.request.Put(fmt.Sprintf("/v1/categories/%d", categoryID), map[string]interface{}{
|
||||
"title": title,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer body.Close()
|
||||
|
||||
var category *Category
|
||||
decoder := json.NewDecoder(body)
|
||||
if err := decoder.Decode(&category); err != nil {
|
||||
return nil, fmt.Errorf("miniflux: response error (%v)", err)
|
||||
}
|
||||
|
||||
return category, nil
|
||||
}
|
||||
|
||||
// DeleteCategory removes a category.
|
||||
func (c *Client) DeleteCategory(categoryID int64) error {
|
||||
body, err := c.request.Delete(fmt.Sprintf("/v1/categories/%d", categoryID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer body.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Feeds gets all feeds.
|
||||
func (c *Client) Feeds() (Feeds, error) {
|
||||
body, err := c.request.Get("/v1/feeds")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer body.Close()
|
||||
|
||||
var feeds Feeds
|
||||
decoder := json.NewDecoder(body)
|
||||
if err := decoder.Decode(&feeds); err != nil {
|
||||
return nil, fmt.Errorf("miniflux: response error (%v)", err)
|
||||
}
|
||||
|
||||
return feeds, nil
|
||||
}
|
||||
|
||||
// Feed gets a new feed.
|
||||
func (c *Client) Feed(feedID int64) (*Feed, error) {
|
||||
body, err := c.request.Get(fmt.Sprintf("/v1/feeds/%d", feedID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer body.Close()
|
||||
|
||||
var feed *Feed
|
||||
decoder := json.NewDecoder(body)
|
||||
if err := decoder.Decode(&feed); err != nil {
|
||||
return nil, fmt.Errorf("miniflux: response error (%v)", err)
|
||||
}
|
||||
|
||||
return feed, nil
|
||||
}
|
||||
|
||||
// CreateFeed creates a new feed.
|
||||
func (c *Client) CreateFeed(url string, categoryID int64) (*Feed, error) {
|
||||
body, err := c.request.Post("/v1/feeds", map[string]interface{}{
|
||||
"feed_url": url,
|
||||
"category_id": categoryID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer body.Close()
|
||||
|
||||
var feed *Feed
|
||||
decoder := json.NewDecoder(body)
|
||||
if err := decoder.Decode(&feed); err != nil {
|
||||
return nil, fmt.Errorf("miniflux: response error (%v)", err)
|
||||
}
|
||||
|
||||
return feed, nil
|
||||
}
|
||||
|
||||
// UpdateFeed updates a feed.
|
||||
func (c *Client) UpdateFeed(feed *Feed) (*Feed, error) {
|
||||
body, err := c.request.Put(fmt.Sprintf("/v1/feeds/%d", feed.ID), feed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer body.Close()
|
||||
|
||||
var f *Feed
|
||||
decoder := json.NewDecoder(body)
|
||||
if err := decoder.Decode(&f); err != nil {
|
||||
return nil, fmt.Errorf("miniflux: response error (%v)", err)
|
||||
}
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// RefreshFeed refresh a feed.
|
||||
func (c *Client) RefreshFeed(feedID int64) error {
|
||||
body, err := c.request.Put(fmt.Sprintf("/v1/feeds/%d/refresh", feedID), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteFeed removes a feed.
|
||||
func (c *Client) DeleteFeed(feedID int64) error {
|
||||
body, err := c.request.Delete(fmt.Sprintf("/v1/feeds/%d", feedID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Entry gets a single feed entry.
|
||||
func (c *Client) Entry(feedID, entryID int64) (*Entry, error) {
|
||||
body, err := c.request.Get(fmt.Sprintf("/v1/feeds/%d/entries/%d", feedID, entryID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer body.Close()
|
||||
|
||||
var entry *Entry
|
||||
decoder := json.NewDecoder(body)
|
||||
if err := decoder.Decode(&entry); err != nil {
|
||||
return nil, fmt.Errorf("miniflux: response error (%v)", err)
|
||||
}
|
||||
|
||||
return entry, nil
|
||||
}
|
||||
|
||||
// Entries gets feed entries.
|
||||
func (c *Client) Entries(feedID int64, filter *Filter) (*EntryResultSet, error) {
|
||||
path := fmt.Sprintf("/v1/feeds/%d/entries", feedID)
|
||||
|
||||
if filter != nil {
|
||||
values := url.Values{}
|
||||
|
||||
if filter.Status != "" {
|
||||
values.Set("status", filter.Status)
|
||||
}
|
||||
|
||||
if filter.Direction != "" {
|
||||
values.Set("direction", filter.Direction)
|
||||
}
|
||||
|
||||
if filter.Order != "" {
|
||||
values.Set("order", filter.Order)
|
||||
}
|
||||
|
||||
if filter.Limit != 0 {
|
||||
values.Set("limit", strconv.Itoa(filter.Limit))
|
||||
}
|
||||
|
||||
if filter.Offset != 0 {
|
||||
values.Set("offset", strconv.Itoa(filter.Offset))
|
||||
}
|
||||
|
||||
path = fmt.Sprintf("%s?%s", path, values.Encode())
|
||||
}
|
||||
|
||||
body, err := c.request.Get(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer body.Close()
|
||||
|
||||
var result EntryResultSet
|
||||
decoder := json.NewDecoder(body)
|
||||
if err := decoder.Decode(&result); err != nil {
|
||||
return nil, fmt.Errorf("miniflux: response error (%v)", err)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// UpdateEntries updates the status of a list of entries.
|
||||
func (c *Client) UpdateEntries(entryIDs []int64, status string) error {
|
||||
type payload struct {
|
||||
EntryIDs []int64 `json:"entry_ids"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
body, err := c.request.Put("/v1/entries", &payload{EntryIDs: entryIDs, Status: status})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
body.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewClient returns a new Client.
|
||||
func NewClient(endpoint, username, password string) *Client {
|
||||
return &Client{request: &request{endpoint: endpoint, username: username, password: password}}
|
||||
}
|
31
vendor/github.com/miniflux/miniflux-go/doc.go
generated
vendored
Normal file
31
vendor/github.com/miniflux/miniflux-go/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2017 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package miniflux implements a client library for the Miniflux REST API.
|
||||
|
||||
Examples
|
||||
|
||||
This code snippet fetch the list of users.
|
||||
|
||||
client := miniflux.NewClient("https://api.example.org", "admin", "secret")
|
||||
users, err := client.Users()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println(users, err)
|
||||
|
||||
This one discover subscriptions on a website.
|
||||
|
||||
subscriptions, err := client.Discover("https://example.org/")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println(subscriptions)
|
||||
|
||||
*/
|
||||
package miniflux
|
131
vendor/github.com/miniflux/miniflux-go/miniflux.go
generated
vendored
Normal file
131
vendor/github.com/miniflux/miniflux-go/miniflux.go
generated
vendored
Normal file
|
@ -0,0 +1,131 @@
|
|||
// Copyright 2017 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package miniflux
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Entry statuses.
|
||||
const (
|
||||
EntryStatusUnread = "unread"
|
||||
EntryStatusRead = "read"
|
||||
EntryStatusRemoved = "removed"
|
||||
)
|
||||
|
||||
// User represents a user in the system.
|
||||
type User struct {
|
||||
ID int64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password,omitempty"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
Theme string `json:"theme"`
|
||||
Language string `json:"language"`
|
||||
Timezone string `json:"timezone"`
|
||||
LastLoginAt *time.Time `json:"last_login_at"`
|
||||
}
|
||||
|
||||
func (u User) String() string {
|
||||
return fmt.Sprintf("#%d - %s (admin=%v)", u.ID, u.Username, u.IsAdmin)
|
||||
}
|
||||
|
||||
// Users represents a list of users.
|
||||
type Users []User
|
||||
|
||||
// Category represents a category in the system.
|
||||
type Category struct {
|
||||
ID int64 `json:"id,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
UserID int64 `json:"user_id,omitempty"`
|
||||
}
|
||||
|
||||
func (c Category) String() string {
|
||||
return fmt.Sprintf("#%d %s", c.ID, c.Title)
|
||||
}
|
||||
|
||||
// Categories represents a list of categories.
|
||||
type Categories []*Category
|
||||
|
||||
// Subscription represents a feed subscription.
|
||||
type Subscription struct {
|
||||
Title string `json:"title"`
|
||||
URL string `json:"url"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
func (s Subscription) String() string {
|
||||
return fmt.Sprintf(`Title="%s", URL="%s", Type="%s"`, s.Title, s.URL, s.Type)
|
||||
}
|
||||
|
||||
// Subscriptions represents a list of subscriptions.
|
||||
type Subscriptions []*Subscription
|
||||
|
||||
// Feed represents a Miniflux feed.
|
||||
type Feed struct {
|
||||
ID int64 `json:"id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
FeedURL string `json:"feed_url"`
|
||||
SiteURL string `json:"site_url"`
|
||||
Title string `json:"title"`
|
||||
CheckedAt time.Time `json:"checked_at,omitempty"`
|
||||
EtagHeader string `json:"etag_header,omitempty"`
|
||||
LastModifiedHeader string `json:"last_modified_header,omitempty"`
|
||||
ParsingErrorMsg string `json:"parsing_error_message,omitempty"`
|
||||
ParsingErrorCount int `json:"parsing_error_count,omitempty"`
|
||||
Category *Category `json:"category,omitempty"`
|
||||
Entries Entries `json:"entries,omitempty"`
|
||||
}
|
||||
|
||||
// Feeds represents a list of feeds.
|
||||
type Feeds []*Feed
|
||||
|
||||
// Entry represents a subscription item in the system.
|
||||
type Entry struct {
|
||||
ID int64 `json:"id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
FeedID int64 `json:"feed_id"`
|
||||
Status string `json:"status"`
|
||||
Hash string `json:"hash"`
|
||||
Title string `json:"title"`
|
||||
URL string `json:"url"`
|
||||
Date time.Time `json:"published_at"`
|
||||
Content string `json:"content"`
|
||||
Author string `json:"author"`
|
||||
Enclosures Enclosures `json:"enclosures,omitempty"`
|
||||
Feed *Feed `json:"feed,omitempty"`
|
||||
Category *Category `json:"category,omitempty"`
|
||||
}
|
||||
|
||||
// Entries represents a list of entries.
|
||||
type Entries []*Entry
|
||||
|
||||
// Enclosure represents an attachment.
|
||||
type Enclosure struct {
|
||||
ID int64 `json:"id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
EntryID int64 `json:"entry_id"`
|
||||
URL string `json:"url"`
|
||||
MimeType string `json:"mime_type"`
|
||||
Size int `json:"size"`
|
||||
}
|
||||
|
||||
// Enclosures represents a list of attachments.
|
||||
type Enclosures []*Enclosure
|
||||
|
||||
// Filter is used to filter entries.
|
||||
type Filter struct {
|
||||
Status string
|
||||
Offset int
|
||||
Limit int
|
||||
Order string
|
||||
Direction string
|
||||
}
|
||||
|
||||
// EntryResultSet represents the response when fetching entries.
|
||||
type EntryResultSet struct {
|
||||
Total int `json:"total"`
|
||||
Entries Entries `json:"entries"`
|
||||
}
|
136
vendor/github.com/miniflux/miniflux-go/request.go
generated
vendored
Normal file
136
vendor/github.com/miniflux/miniflux-go/request.go
generated
vendored
Normal file
|
@ -0,0 +1,136 @@
|
|||
// Copyright 2017 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package miniflux
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
userAgent = "Miniflux Client Library <https://github.com/miniflux/miniflux-go>"
|
||||
defaultTimeout = 80
|
||||
)
|
||||
|
||||
var (
|
||||
errNotAuthorized = errors.New("miniflux: unauthorized (bad credentials)")
|
||||
errForbidden = errors.New("miniflux: access forbidden")
|
||||
errServerError = errors.New("miniflux: internal server error")
|
||||
)
|
||||
|
||||
type errorResponse struct {
|
||||
ErrorMessage string `json:"error_message"`
|
||||
}
|
||||
|
||||
type request struct {
|
||||
endpoint string
|
||||
username string
|
||||
password string
|
||||
}
|
||||
|
||||
func (r *request) Get(path string) (io.ReadCloser, error) {
|
||||
return r.execute(http.MethodGet, path, nil)
|
||||
}
|
||||
|
||||
func (r *request) Post(path string, data interface{}) (io.ReadCloser, error) {
|
||||
return r.execute(http.MethodPost, path, data)
|
||||
}
|
||||
|
||||
func (r *request) Put(path string, data interface{}) (io.ReadCloser, error) {
|
||||
return r.execute(http.MethodPut, path, data)
|
||||
}
|
||||
|
||||
func (r *request) Delete(path string) (io.ReadCloser, error) {
|
||||
return r.execute(http.MethodDelete, path, nil)
|
||||
}
|
||||
|
||||
func (r *request) execute(method, path string, data interface{}) (io.ReadCloser, error) {
|
||||
u, err := url.Parse(r.endpoint + path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
request := &http.Request{
|
||||
URL: u,
|
||||
Method: method,
|
||||
Header: r.buildHeaders(),
|
||||
}
|
||||
request.SetBasicAuth(r.username, r.password)
|
||||
|
||||
if data != nil {
|
||||
request.Body = ioutil.NopCloser(bytes.NewBuffer(r.toJSON(data)))
|
||||
}
|
||||
|
||||
client := r.buildClient()
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch response.StatusCode {
|
||||
case http.StatusUnauthorized:
|
||||
return nil, errNotAuthorized
|
||||
case http.StatusForbidden:
|
||||
return nil, errForbidden
|
||||
case http.StatusInternalServerError:
|
||||
return nil, errServerError
|
||||
case http.StatusBadRequest:
|
||||
defer response.Body.Close()
|
||||
|
||||
var resp errorResponse
|
||||
decoder := json.NewDecoder(response.Body)
|
||||
if err := decoder.Decode(&resp); err != nil {
|
||||
return nil, fmt.Errorf("miniflux: bad request error (%v)", err)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("miniflux: bad request (%s)", resp.ErrorMessage)
|
||||
}
|
||||
|
||||
if response.StatusCode >= 400 {
|
||||
return nil, fmt.Errorf("miniflux: server error (statusCode=%d)", response.StatusCode)
|
||||
}
|
||||
|
||||
return response.Body, nil
|
||||
}
|
||||
|
||||
func (r *request) buildClient() http.Client {
|
||||
return http.Client{
|
||||
Timeout: time.Duration(defaultTimeout * time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *request) buildHeaders() http.Header {
|
||||
headers := make(http.Header)
|
||||
headers.Add("User-Agent", userAgent)
|
||||
headers.Add("Content-Type", "application/json")
|
||||
headers.Add("Accept", "application/json")
|
||||
return headers
|
||||
}
|
||||
|
||||
func (r *request) toJSON(v interface{}) []byte {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
log.Println("Unable to convert interface to JSON:", err)
|
||||
return []byte("")
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func newRequest(endpoint, username, password string) *request {
|
||||
return &request{
|
||||
endpoint: endpoint,
|
||||
username: username,
|
||||
password: password,
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue