Skip to content

Commit

Permalink
cmd/np: add -f flag to read config file (#21)
Browse files Browse the repository at this point in the history
Fixes #20
  • Loading branch information
matm authored Dec 20, 2022
1 parent a6ea8c5 commit ab144ce
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 49 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
v 1.0.0
- cmd/np: add -f flag to read config file. #20
- Implement create payment from invoice. #15
- Implement create invoice. #14
- Implement get/update payment estimate. #16
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ Usage of np:
pay amount for new payment/invoice (default 2)
-c show list of selected currencies
-d turn debugging on
-f string
JSON config file to use
-i new invoice
-l list all payments
-n new payment
Expand Down
42 changes: 23 additions & 19 deletions cmd/np/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,7 @@ import (
)

func main() {
f, err := os.Open("conf.json")
if err != nil {
log.Fatal(err)
}
defer f.Close()
err = config.Load(f)
if err != nil {
log.Fatal(err)
}
core.UseBaseURL(core.SandBoxBaseURL)
core.UseClient(core.NewHTTPClient())

cfgFile := flag.String("f", "", "JSON config file to use")
paymentID := flag.String("p", "", "status of payment ID")
newPayment := flag.Bool("n", false, "new payment")
payAmount := flag.Float64("a", 2.0, "pay amount for new payment/invoice")
Expand All @@ -37,6 +26,21 @@ func main() {

flag.Parse()

if *cfgFile == "" {
log.Fatal("please specify a JSON config file with -f")
}
f, err := os.Open(*cfgFile)
if err != nil {
log.Fatal(err)
}
defer f.Close()
err = config.Load(f)
if err != nil {
log.Fatal(err)
}
core.UseBaseURL(core.BaseURL(config.Server()))
core.UseClient(core.NewHTTPClient())

if *debug {
core.WithDebug(true)
}
Expand All @@ -54,25 +58,25 @@ func main() {
return
}

fmt.Println("Sandbox:", core.BaseURL() == core.SandBoxBaseURL)
fmt.Fprintln(os.Stderr, "Sandbox:", config.Server() == core.SandBoxBaseURL)
st, err := core.Status()
if err != nil {
log.Fatal(err)
}
fmt.Println("API Status:", st)
fmt.Fprintln(os.Stderr, "API Status:", st)

if *showCurrencies {
cs, err := currencies.Selected()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%d selected (checked) crypto currencies: %v\n", len(cs), cs)
fmt.Fprintf(os.Stderr, "%d selected (checked) crypto currencies: %v\n", len(cs), cs)
return
}

if *listPayments {
limit := 5
fmt.Printf("Showing last %d payments only:\n", limit)
fmt.Fprintf(os.Stderr, "Showing last %d payments only:\n", limit)
all, err := payments.List(&payments.ListOption{
Limit: limit,
})
Expand All @@ -97,7 +101,7 @@ func main() {
OrderDescription: "Some useful tool",
},
}
fmt.Printf("Creating a %.2f payment ...\n", pa.PriceAmount)
fmt.Fprintf(os.Stderr, "Creating a %.2f payment ...\n", pa.PriceAmount)
pay, err := payments.New(pa)
if err != nil {
log.Fatal(err)
Expand All @@ -122,7 +126,7 @@ func main() {
CancelURL: "http://mycancel",
SuccessURL: "http://mysuccess",
}
fmt.Printf("Creating a %.2f invoice ...\n", pa.PriceAmount)
fmt.Fprintf(os.Stderr, "Creating a %.2f invoice ...\n", pa.PriceAmount)
pay, err := payments.NewInvoice(pa)
if err != nil {
log.Fatal(err)
Expand All @@ -140,7 +144,7 @@ func main() {
InvoiceID: *newPaymentFromInvoice,
PayCurrency: "xmr",
}
fmt.Printf("Creating a payment from invoice %q...\n", pa.InvoiceID)
fmt.Fprintf(os.Stderr, "Creating a payment from invoice %q...\n", pa.InvoiceID)
pay, err := payments.NewFromInvoice(pa)
if err != nil {
log.Fatal(err)
Expand Down
41 changes: 39 additions & 2 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,56 @@ package config

import (
"encoding/json"
"errors"
"io"
"net/url"

"github.com/rotisserie/eris"
)

type credentials struct {
APIKey string `json:"apiKey"`
Login string `json:"login"`
Password string `json:"password"`
APIKey string `json:"apiKey"`
Server string `json:"server"`
}

var conf credentials

func configErr(err error) error {
return eris.Wrap(err, "config")
}

// Load parses a JSON file to get the required credentials to operate NOWPayment's API.
func Load(r io.Reader) error {
if r == nil {
return configErr(errors.New("nil reader"))
}
conf = credentials{}
d := json.NewDecoder(r)
err := d.Decode(&conf)
return eris.Wrap(err, "decode config")
if err != nil {
return configErr(err)
}
// Sanity checks.
if conf.APIKey == "" {
return configErr(errors.New("API key is missing"))
}
if conf.Login == "" {
return configErr(errors.New("login info missing"))
}
if conf.Password == "" {
return configErr(errors.New("password info missing"))
}
if conf.Server == "" {
return configErr(errors.New("server URL missing"))
} else {
_, err := url.Parse(conf.Server)
if err != nil {
return configErr(errors.New("server URL parsing"))
}
}
return nil
}

// Login returns the email address to use with the API.
Expand All @@ -36,3 +68,8 @@ func Password() string {
func APIKey() string {
return conf.APIKey
}

// Server returns URL to connect to the API service.
func Server() string {
return conf.Server
}
111 changes: 111 additions & 0 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package config

import (
"io"
"strings"
"testing"
)

func TestLoad(t *testing.T) {
emptyAPIKeyCfg := `{"server":"http://some.tld","login":"mylogin","password":"mypass"}`
emptyLoginCfg := `{"server":"http://some.tld","apiKey":"key","password":"mypass"}`
emptyPasswordCfg := `{"server":"http://some.tld","login":"mylogin","apiKey":"key"}`
emptyServerCfg := `{"apiKey":"key","login":"mylogin","password":"mypass"}`
tests := []struct {
name string
r io.Reader
wantErr bool
}{
{"nil reader", nil, true},
{"bad config", strings.NewReader("nojson"), true},
{"valid config", strings.NewReader(validCfg), false},
{"empty API key", strings.NewReader(emptyAPIKeyCfg), true},
{"empty login", strings.NewReader(emptyLoginCfg), true},
{"empty password", strings.NewReader(emptyPasswordCfg), true},
{"empty server", strings.NewReader(emptyServerCfg), true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := Load(tt.r); (err != nil) != tt.wantErr {
t.Errorf("Load() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

var validCfg = `
{
"server": "http://some.tld",
"login": "mylogin",
"password": "mypass",
"apiKey": "key"
}
`

func TestLogin(t *testing.T) {
Load(strings.NewReader(validCfg))
tests := []struct {
name string
want string
}{
{"login value", "mylogin"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := Login(); got != tt.want {
t.Errorf("Login() = %v, want %v", got, tt.want)
}
})
}
}

func TestPassword(t *testing.T) {
Load(strings.NewReader(validCfg))
tests := []struct {
name string
want string
}{
{"password value", "mypass"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := Password(); got != tt.want {
t.Errorf("Password() = %v, want %v", got, tt.want)
}
})
}
}

func TestAPIKey(t *testing.T) {
Load(strings.NewReader(validCfg))
tests := []struct {
name string
want string
}{
{"key value", "key"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := APIKey(); got != tt.want {
t.Errorf("APIKey() = %v, want %v", got, tt.want)
}
})
}
}

func TestServer(t *testing.T) {
Load(strings.NewReader(validCfg))
tests := []struct {
name string
want string
}{
{"server url", "http://some.tld"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := Server(); got != tt.want {
t.Errorf("Server() = %v, want %v", got, tt.want)
}
})
}
}
14 changes: 5 additions & 9 deletions pkg/core/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ import (
"github.com/rotisserie/eris"
)

type baseURL string
// BaseURL is the URL to NOWPayment's service.
type BaseURL string

const (
// ProductionBaseURL is the URL to the production service.
ProductionBaseURL baseURL = "https://api.nowpayments.io/v1"
ProductionBaseURL BaseURL = "https://api.nowpayments.io/v1"
// SandBoxBaseURL is the URL to the sandbox service.
SandBoxBaseURL = "https://api-sandbox.nowpayments.io/v1"
)
Expand Down Expand Up @@ -52,7 +53,7 @@ var routes map[string]routeAttr = map[string]routeAttr{
}

var (
defaultURL baseURL = SandBoxBaseURL
defaultURL BaseURL = SandBoxBaseURL
)

var debug = false
Expand All @@ -63,15 +64,10 @@ func WithDebug(d bool) {
}

// UseBaseURL sets the base URL to use to connect to NOWPayment's API.
func UseBaseURL(b baseURL) {
func UseBaseURL(b BaseURL) {
defaultURL = b
}

// BaseURL returns the base URL used to connect to NOWPayment's API.
func BaseURL() string {
return string(defaultURL)
}

// HTTPSend sends to endpoint with an optional request body and get the HTTP
// response result in into.
func HTTPSend(p *SendParams) error {
Expand Down
21 changes: 2 additions & 19 deletions pkg/core/router_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func newResponseOK(body string) *http.Response {
func conf() *strings.Reader {
return strings.NewReader(`
{
"login":"l","password":"p","apiKey":"key"
"login":"l","password":"p","apiKey":"key","server":"http://some.tld"
}
`)
}
Expand Down Expand Up @@ -197,7 +197,7 @@ func TestWithDebug(t *testing.T) {

func TestUseBaseURL(t *testing.T) {
type args struct {
b baseURL
b BaseURL
}
tests := []struct {
name string
Expand All @@ -217,20 +217,3 @@ func TestUseBaseURL(t *testing.T) {
})
}
}

func TestBaseURL(t *testing.T) {
tests := []struct {
name string
want string
}{
{"get url", "this"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
UseBaseURL(baseURL("this"))
if got := BaseURL(); got != tt.want {
t.Errorf("BaseURL() = %v, want %v", got, tt.want)
}
})
}
}

0 comments on commit ab144ce

Please sign in to comment.