Initial commit
This commit is contained in:
commit
ad529187f5
6
.dependabot/config.yml
Normal file
6
.dependabot/config.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
version: 1
|
||||||
|
|
||||||
|
update_configs:
|
||||||
|
- package_manager: "go:modules"
|
||||||
|
directory: "/"
|
||||||
|
update_schedule: "daily"
|
16
.github/workflows/main.yml
vendored
Normal file
16
.github/workflows/main.yml
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
name: Test
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: go get
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: go test -v ./...
|
18
.gitignore
vendored
Normal file
18
.gitignore
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Editors related
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
# vendor/
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Sighery
|
||||||
|
|
||||||
|
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.
|
54
README.md
Normal file
54
README.md
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
# Unofficial Golang library for the Njalla API
|
||||||
|
|
||||||
|
[Njalla][] is a privacy-oriented domain name registration service. Recently
|
||||||
|
they released their [official API][].
|
||||||
|
|
||||||
|
This Golang library covers _some_ methods of that API. For the moment, those
|
||||||
|
are:
|
||||||
|
* `list-domains`
|
||||||
|
* `get-domain`
|
||||||
|
* `list-records`
|
||||||
|
* `add-record`
|
||||||
|
* `edit-record`
|
||||||
|
* `remove-record`
|
||||||
|
|
||||||
|
**TO NOTE**: Even though `record` methods are implemented, I'm fairly certain
|
||||||
|
they'll fail (silently or not) in some cases. I deal mostly with `TXT`, `MX`,
|
||||||
|
`A` and `AAAA` DNS records. Some records have different/more variables, and
|
||||||
|
since I don't use them I decided against implementing them. Chances are the
|
||||||
|
methods will fail when trying to deal with those types of records (like `SSH`
|
||||||
|
records).
|
||||||
|
|
||||||
|
The code is fairly simple, and all the methods are tested by using mocks on
|
||||||
|
the API request. The mocked returned data is based on the same data the API
|
||||||
|
returns.
|
||||||
|
|
||||||
|
These methods cover my needs, but feel free to send in a PR to add more (or to
|
||||||
|
cover all types of DNS records), as long as they're all tested and documented.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
```golang
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Sighery/gonjalla"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
token := "api-token"
|
||||||
|
domain := "your-domain"
|
||||||
|
|
||||||
|
records, err := ListRecords(token, domain)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(records)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[Njalla]: https://njal.la
|
||||||
|
[official API]: https://njal.la/api/
|
58
domains.go
Normal file
58
domains.go
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package gonjalla
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Domain struct contains data returned by `list-domains` and `get-domains`
|
||||||
|
type Domain struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Expiry time.Time `json:"expiry"`
|
||||||
|
Locked *bool `json:"locked,omitempty"`
|
||||||
|
Mailforwarding *bool `json:"mailforwarding,omitempty"`
|
||||||
|
MaxNameservers *int `json:"max_nameservers,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListDomains returns a listing of domains with minimal data
|
||||||
|
func ListDomains(token string) ([]Domain, error) {
|
||||||
|
params := map[string]interface{}{}
|
||||||
|
|
||||||
|
data, err := Request(token, "list-domains", params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Domains []Domain `json:"domains"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var response Response
|
||||||
|
err = json.Unmarshal(data, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Domains, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDomain returns detailed information for each domain
|
||||||
|
func GetDomain(token string, domain string) (Domain, error) {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"domain": domain,
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := Request(token, "get-domain", params)
|
||||||
|
if err != nil {
|
||||||
|
return Domain{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var domainStruct Domain
|
||||||
|
err = json.Unmarshal(data, &domainStruct)
|
||||||
|
if err != nil {
|
||||||
|
return Domain{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return domainStruct, nil
|
||||||
|
}
|
163
domains_test.go
Normal file
163
domains_test.go
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
package gonjalla
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/Sighery/gonjalla/mocks"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestListDomainsExpected(t *testing.T) {
|
||||||
|
token := "test-token"
|
||||||
|
Client = &mocks.MockClient{}
|
||||||
|
|
||||||
|
testData := `{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"result": {
|
||||||
|
"domains": [
|
||||||
|
{
|
||||||
|
"name": "testing1.com",
|
||||||
|
"status": "active",
|
||||||
|
"expiry": "2021-02-20T19:38:48Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "testing2.com",
|
||||||
|
"status": "inactive",
|
||||||
|
"expiry": "2021-02-20T19:38:48Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
r := ioutil.NopCloser(bytes.NewReader([]byte(testData)))
|
||||||
|
|
||||||
|
mocks.GetDoFunc = func(*http.Request) (*http.Response, error) {
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: 200,
|
||||||
|
Body: r,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
domains, err := ListDomains(token)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedTime, _ := time.Parse(time.RFC3339, "2021-02-20T19:38:48Z")
|
||||||
|
|
||||||
|
expected := []Domain{
|
||||||
|
{
|
||||||
|
Name: "testing1.com",
|
||||||
|
Status: "active",
|
||||||
|
Expiry: expectedTime,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "testing2.com",
|
||||||
|
Status: "inactive",
|
||||||
|
Expiry: expectedTime,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, domains, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListDomainsError(t *testing.T) {
|
||||||
|
token := "test-token"
|
||||||
|
Client = &mocks.MockClient{}
|
||||||
|
|
||||||
|
testData := `{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"error": {
|
||||||
|
"code": 0,
|
||||||
|
"message": "Testing error"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
r := ioutil.NopCloser(bytes.NewReader([]byte(testData)))
|
||||||
|
|
||||||
|
mocks.GetDoFunc = func(*http.Request) (*http.Response, error) {
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: 200,
|
||||||
|
Body: r,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
domains, err := ListDomains(token)
|
||||||
|
assert.Nil(t, domains)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDomainExpected(t *testing.T) {
|
||||||
|
token := "test-token"
|
||||||
|
domain := "testing.com"
|
||||||
|
Client = &mocks.MockClient{}
|
||||||
|
|
||||||
|
testData := `{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"result": {
|
||||||
|
"name": "testing.com",
|
||||||
|
"status": "active",
|
||||||
|
"expiry": "2021-02-20T19:38:48Z",
|
||||||
|
"locked": true,
|
||||||
|
"mailforwarding": false,
|
||||||
|
"max_nameservers": 10
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
r := ioutil.NopCloser(bytes.NewReader([]byte(testData)))
|
||||||
|
|
||||||
|
mocks.GetDoFunc = func(*http.Request) (*http.Response, error) {
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: 200,
|
||||||
|
Body: r,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := GetDomain(token, domain)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedTime, _ := time.Parse(time.RFC3339, "2021-02-20T19:38:48Z")
|
||||||
|
locked := true
|
||||||
|
mailforwarding := false
|
||||||
|
maxNameservers := 10
|
||||||
|
|
||||||
|
expected := Domain{
|
||||||
|
Name: domain,
|
||||||
|
Status: "active",
|
||||||
|
Expiry: expectedTime,
|
||||||
|
Locked: &locked,
|
||||||
|
Mailforwarding: &mailforwarding,
|
||||||
|
MaxNameservers: &maxNameservers,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, result, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDomainError(t *testing.T) {
|
||||||
|
token := "test-token"
|
||||||
|
domain := "testing.com"
|
||||||
|
Client = &mocks.MockClient{}
|
||||||
|
|
||||||
|
testData := `{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"error": {
|
||||||
|
"code": 0,
|
||||||
|
"message": "Testing error"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
r := ioutil.NopCloser(bytes.NewReader([]byte(testData)))
|
||||||
|
|
||||||
|
mocks.GetDoFunc = func(*http.Request) (*http.Response, error) {
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: 200,
|
||||||
|
Body: r,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := GetDomain(token, domain)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
5
go.mod
Normal file
5
go.mod
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
module github.com/Sighery/gonjalla
|
||||||
|
|
||||||
|
go 1.14
|
||||||
|
|
||||||
|
require github.com/stretchr/testify v1.5.1
|
11
go.sum
Normal file
11
go.sum
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
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/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
18
mocks/mocks.go
Normal file
18
mocks/mocks.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package mocks
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
// MockClient is the mock HTTP client used for tests
|
||||||
|
type MockClient struct {
|
||||||
|
DoFunc func(req *http.Request) (*http.Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// GetDoFunc fetches the mock client's `Do` func
|
||||||
|
GetDoFunc func(req *http.Request) (*http.Response, error)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Do is the mock client's `Do` func
|
||||||
|
func (m *MockClient) Do(req *http.Request) (*http.Response, error) {
|
||||||
|
return GetDoFunc(req)
|
||||||
|
}
|
86
provider.go
Normal file
86
provider.go
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package gonjalla
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type request struct {
|
||||||
|
Method string `json:"method"`
|
||||||
|
Params map[string]interface{} `json:"params"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const endpoint string = "https://njal.la/api/1/"
|
||||||
|
|
||||||
|
// HTTPClient interface. Useful for mocked unit tests later on.
|
||||||
|
type HTTPClient interface {
|
||||||
|
Do(req *http.Request) (*http.Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Client used in all requests by Request. Can be overwritten for tests.
|
||||||
|
Client HTTPClient
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Client = &http.Client{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request common function for all of Njalla's API.
|
||||||
|
// Njalla's API uses JSON-RPC, and contains just one endpoint.
|
||||||
|
// The endpoint is POST only, and takes in a JSON in the body, with two
|
||||||
|
// arguments, check the `request` struct for more info.
|
||||||
|
// The `params` argument is variable. Some methods require no parameters,
|
||||||
|
// (like `list-domains`), while other methods require parameters (like
|
||||||
|
// `get-domain` which requires `domain: string`).
|
||||||
|
func Request(
|
||||||
|
token string, method string, params map[string]interface{},
|
||||||
|
) ([]byte, error) {
|
||||||
|
token = fmt.Sprintf("Njalla %s", token)
|
||||||
|
|
||||||
|
body, err := json.Marshal(
|
||||||
|
request{Method: method, Params: params},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(body))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Add("Authorization", token)
|
||||||
|
|
||||||
|
resp, err := Client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
jsonData, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make(map[string]interface{})
|
||||||
|
|
||||||
|
err = json.Unmarshal(jsonData, &data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result, ok := data["result"]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Missing result %s", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
unwrapped, err := json.Marshal(result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return unwrapped, nil
|
||||||
|
}
|
111
records.go
Normal file
111
records.go
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
package gonjalla
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
// Record struct contains data returned by `list-records`
|
||||||
|
type Record struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
TTL int `json:"ttl"`
|
||||||
|
Priority *int `json:"prio,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRecords returns a listing of all records for a given domain
|
||||||
|
func ListRecords(token string, domain string) ([]Record, error) {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"domain": domain,
|
||||||
|
}
|
||||||
|
data, err := Request(token, "list-records", params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Records []Record `json:"records"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var response Response
|
||||||
|
err = json.Unmarshal(data, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Records, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRecord adds a given record to a given domain.
|
||||||
|
// Returns a new Record struct, containing the response from the API if
|
||||||
|
// successful. This response will have some fields like ID (which can only
|
||||||
|
// be known after the execution) filled.
|
||||||
|
func AddRecord(token string, domain string, record Record) (Record, error) {
|
||||||
|
marshal, err := json.Marshal(record)
|
||||||
|
if err != nil {
|
||||||
|
return Record{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"domain": domain,
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(marshal, ¶ms)
|
||||||
|
if err != nil {
|
||||||
|
return Record{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := Request(token, "add-record", params)
|
||||||
|
if err != nil {
|
||||||
|
return Record{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var response Record
|
||||||
|
err = json.Unmarshal(data, &response)
|
||||||
|
if err != nil {
|
||||||
|
return Record{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveRecord removes a given record from a given domain.
|
||||||
|
// If there are no errors it will return `nil`.
|
||||||
|
func RemoveRecord(token string, domain string, id int) error {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"domain": domain,
|
||||||
|
"id": id,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := Request(token, "remove-record", params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditRecord edits a record for a given domain.
|
||||||
|
// This function is fairly dumb. It takes in a `Record` struct, and uses all
|
||||||
|
// its filled fields to send to Njalla.
|
||||||
|
// So, if you want to only change a given field, get the `Record` object from
|
||||||
|
// say ListRecords, change the one field you want, and then pass that here.
|
||||||
|
func EditRecord(token string, domain string, record Record) error {
|
||||||
|
marshal, err := json.Marshal(record)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"domain": domain,
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(marshal, ¶ms)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = Request(token, "edit-record", params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
342
records_test.go
Normal file
342
records_test.go
Normal file
|
@ -0,0 +1,342 @@
|
||||||
|
package gonjalla
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/Sighery/gonjalla/mocks"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestListRecordsExpected(t *testing.T) {
|
||||||
|
token := "test-token"
|
||||||
|
domain := "testing.com"
|
||||||
|
Client = &mocks.MockClient{}
|
||||||
|
|
||||||
|
testData := `{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"result": {
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"id": 1337,
|
||||||
|
"name": "_acme-challenge",
|
||||||
|
"type": "TXT",
|
||||||
|
"content": "long-string",
|
||||||
|
"ttl": 10800
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1338,
|
||||||
|
"name": "@",
|
||||||
|
"type": "A",
|
||||||
|
"content": "1.2.3.4",
|
||||||
|
"ttl": 3600
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1339,
|
||||||
|
"name": "@",
|
||||||
|
"type": "AAAA",
|
||||||
|
"content": "2001:0DB8:0000:0000:0000:8A2E:0370:7334",
|
||||||
|
"ttl": 900
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1340,
|
||||||
|
"name": "@",
|
||||||
|
"type": "MX",
|
||||||
|
"content": "mail.protonmail.ch",
|
||||||
|
"ttl": 300,
|
||||||
|
"prio": 10
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
r := ioutil.NopCloser(bytes.NewReader([]byte(testData)))
|
||||||
|
|
||||||
|
mocks.GetDoFunc = func(*http.Request) (*http.Response, error) {
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: 200,
|
||||||
|
Body: r,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
records, err := ListRecords(token, domain)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
priority := 10
|
||||||
|
|
||||||
|
expected := []Record{
|
||||||
|
{
|
||||||
|
ID: 1337,
|
||||||
|
Name: "_acme-challenge",
|
||||||
|
Type: "TXT",
|
||||||
|
Content: "long-string",
|
||||||
|
TTL: 10800,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 1338,
|
||||||
|
Name: "@",
|
||||||
|
Type: "A",
|
||||||
|
Content: "1.2.3.4",
|
||||||
|
TTL: 3600,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 1339,
|
||||||
|
Name: "@",
|
||||||
|
Type: "AAAA",
|
||||||
|
Content: "2001:0DB8:0000:0000:0000:8A2E:0370:7334",
|
||||||
|
TTL: 900,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 1340,
|
||||||
|
Name: "@",
|
||||||
|
Type: "MX",
|
||||||
|
Content: "mail.protonmail.ch",
|
||||||
|
TTL: 300,
|
||||||
|
Priority: &priority,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, records, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListRecordsError(t *testing.T) {
|
||||||
|
token := "test-token"
|
||||||
|
domain := "testing.com"
|
||||||
|
Client = &mocks.MockClient{}
|
||||||
|
|
||||||
|
testData := `{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"error": {
|
||||||
|
"code": 0,
|
||||||
|
"message": "Testing error"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
r := ioutil.NopCloser(bytes.NewReader([]byte(testData)))
|
||||||
|
|
||||||
|
mocks.GetDoFunc = func(*http.Request) (*http.Response, error) {
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: 200,
|
||||||
|
Body: r,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
records, err := ListRecords(token, domain)
|
||||||
|
if records != nil {
|
||||||
|
t.Error("records isn't nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddRecordExpected(t *testing.T) {
|
||||||
|
token := "test-token"
|
||||||
|
domain := "testing.com"
|
||||||
|
Client = &mocks.MockClient{}
|
||||||
|
|
||||||
|
testData := `{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"result": {
|
||||||
|
"id": 1337,
|
||||||
|
"name": "@",
|
||||||
|
"type": "MX",
|
||||||
|
"content": "testing.com",
|
||||||
|
"ttl": 10800,
|
||||||
|
"prio": 10
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
r := ioutil.NopCloser(bytes.NewReader([]byte(testData)))
|
||||||
|
|
||||||
|
mocks.GetDoFunc = func(*http.Request) (*http.Response, error) {
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: 200,
|
||||||
|
Body: r,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
priority := 10
|
||||||
|
|
||||||
|
adding := Record{
|
||||||
|
Name: "@",
|
||||||
|
Type: "MX",
|
||||||
|
Content: "testing.com",
|
||||||
|
TTL: 10800,
|
||||||
|
Priority: &priority,
|
||||||
|
}
|
||||||
|
|
||||||
|
record, err := AddRecord(token, domain, adding)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := Record{
|
||||||
|
ID: 1337,
|
||||||
|
Name: "@",
|
||||||
|
Type: "MX",
|
||||||
|
Content: "testing.com",
|
||||||
|
TTL: 10800,
|
||||||
|
Priority: &priority,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, record, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddRecordError(t *testing.T) {
|
||||||
|
token := "test-token"
|
||||||
|
domain := "testing.com"
|
||||||
|
Client = &mocks.MockClient{}
|
||||||
|
|
||||||
|
testData := `{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"error": {
|
||||||
|
"code": 0,
|
||||||
|
"message": "Testing error"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
r := ioutil.NopCloser(bytes.NewReader([]byte(testData)))
|
||||||
|
|
||||||
|
mocks.GetDoFunc = func(*http.Request) (*http.Response, error) {
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: 200,
|
||||||
|
Body: r,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
priority := 10
|
||||||
|
adding := Record{
|
||||||
|
Name: "@",
|
||||||
|
Type: "MX",
|
||||||
|
Content: "testing.com",
|
||||||
|
TTL: 10800,
|
||||||
|
Priority: &priority,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := AddRecord(token, domain, adding)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveRecordExpected(t *testing.T) {
|
||||||
|
token := "test-token"
|
||||||
|
domain := "testing.com"
|
||||||
|
id := 1337
|
||||||
|
Client = &mocks.MockClient{}
|
||||||
|
|
||||||
|
testData := `{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"result": {}
|
||||||
|
}`
|
||||||
|
r := ioutil.NopCloser(bytes.NewReader([]byte(testData)))
|
||||||
|
|
||||||
|
mocks.GetDoFunc = func(*http.Request) (*http.Response, error) {
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: 200,
|
||||||
|
Body: r,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := RemoveRecord(token, domain, id)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveRecordError(t *testing.T) {
|
||||||
|
token := "test-token"
|
||||||
|
domain := "testing.com"
|
||||||
|
id := 1337
|
||||||
|
Client = &mocks.MockClient{}
|
||||||
|
|
||||||
|
testData := `{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"error": {
|
||||||
|
"code": 0,
|
||||||
|
"message": "Testing error"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
r := ioutil.NopCloser(bytes.NewReader([]byte(testData)))
|
||||||
|
|
||||||
|
mocks.GetDoFunc = func(*http.Request) (*http.Response, error) {
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: 200,
|
||||||
|
Body: r,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := RemoveRecord(token, domain, id)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEditRecordExpected(t *testing.T) {
|
||||||
|
token := "test-token"
|
||||||
|
domain := "testing.com"
|
||||||
|
Client = &mocks.MockClient{}
|
||||||
|
|
||||||
|
testData := `{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"result": {
|
||||||
|
"id": 1337,
|
||||||
|
"name": "@",
|
||||||
|
"type": "MX",
|
||||||
|
"content": "testing.com",
|
||||||
|
"ttl": 10800,
|
||||||
|
"prio": 10
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
r := ioutil.NopCloser(bytes.NewReader([]byte(testData)))
|
||||||
|
|
||||||
|
mocks.GetDoFunc = func(*http.Request) (*http.Response, error) {
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: 200,
|
||||||
|
Body: r,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
priority := 10
|
||||||
|
editing := Record{
|
||||||
|
ID: 1337,
|
||||||
|
Name: "@",
|
||||||
|
Type: "MX",
|
||||||
|
Content: "testing.com",
|
||||||
|
TTL: 10800,
|
||||||
|
Priority: &priority,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := EditRecord(token, domain, editing)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEditRecordError(t *testing.T) {
|
||||||
|
token := "test-token"
|
||||||
|
domain := "testing.com"
|
||||||
|
Client = &mocks.MockClient{}
|
||||||
|
|
||||||
|
testData := `{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"error": {
|
||||||
|
"code": 0,
|
||||||
|
"message": "Testing error"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
r := ioutil.NopCloser(bytes.NewReader([]byte(testData)))
|
||||||
|
|
||||||
|
mocks.GetDoFunc = func(*http.Request) (*http.Response, error) {
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: 200,
|
||||||
|
Body: r,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
priority := 10
|
||||||
|
editing := Record{
|
||||||
|
ID: 1337,
|
||||||
|
Name: "@",
|
||||||
|
Type: "MX",
|
||||||
|
Content: "testing.com",
|
||||||
|
TTL: 10800,
|
||||||
|
Priority: &priority,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := EditRecord(token, domain, editing)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
Loading…
Reference in a new issue