Add first integration test

This commit is contained in:
Frédéric Guillot 2017-11-25 10:40:23 -08:00
parent 71bf7e4358
commit 142e8b3e0c
14 changed files with 891 additions and 12 deletions

View File

@ -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
View File

@ -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

View File

@ -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
View 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)
}
}

12
main.go
View File

@ -116,8 +116,16 @@ func main() {
}
if *flagCreateAdmin {
user := &model.User{IsAdmin: true}
user.Username, user.Password = askCredentials()
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)

View File

@ -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:"-"`
}

View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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,
}
}