forked from TrueCloudLab/lego
Add DNS Provider for inwx (#687)
This commit is contained in:
parent
286c44337e
commit
42d8637d87
28 changed files with 4444 additions and 1 deletions
|
@ -20,7 +20,7 @@ It is up to you to make a strong point about your proposal and convince us of th
|
|||
|
||||
## Pull requests
|
||||
|
||||
Patches, new features and improvements are a great way to help the project.
|
||||
Patches, new features and improvements are a great way to help the project.
|
||||
Please keep them focused on one thing and do not include unrelated commits.
|
||||
|
||||
All pull requests which alter the behaviour of the program, add new behaviour or somehow alter code in a non-trivial way should **always** include tests.
|
||||
|
@ -60,6 +60,7 @@ owners to license your work under the terms of the [MIT License](LICENSE).
|
|||
| Go Daddy | `godaddy` | [documentation](https://developer.godaddy.com/doc/endpoint/domains) | - |
|
||||
| hosting.de | `hostingde` | [documentation](https://www.hosting.de/api/#dns) | - |
|
||||
| Internet Initiative Japan | `iij` | [documentation](http://manual.iij.jp/p2/pubapi/) | [Go client](https://github.com/iij/doapi) |
|
||||
| INWX | `inwx` | [documentation](https://www.inwx.de/en/help/apidoc) | [Go client](https://github.com/smueller18/goinwx) |
|
||||
| Lightsail | `lightsail` | ? | [Go client](https://github.com/aws/aws-sdk-go/aws) |
|
||||
| Linode (deprecated) | `linode` | [documentation](https://www.linode.com/api/dns) | [Go client](https://github.com/timewasted/linode) |
|
||||
| Linodev4 | `linodev4` | [documentation](https://developers.linode.com/api/v4) | [Go client](https://github.com/linode/linodego) |
|
||||
|
|
33
Gopkg.lock
generated
33
Gopkg.lock
generated
|
@ -191,6 +191,14 @@
|
|||
revision = "0863d555d5198557e0bf2b61b6c59a873ab0173a"
|
||||
version = "v0.11.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:aa3ed0a71c4e66e4ae6486bf97a3f4cab28edc78df2e50c5ad01dc7d91604b88"
|
||||
name = "github.com/fatih/structs"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "4966fc68f5b7593aafa6cbbba2d65ec6e1416047"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:74d9b0a7b4107b41e0ade759fac64502876f82d29fb23d77b3dd24b194ee3dd5"
|
||||
name = "github.com/go-ini/ini"
|
||||
|
@ -265,6 +273,14 @@
|
|||
revision = "59fac5042749a5afb9af70e813da1dd5474f0167"
|
||||
version = "1.0.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:ec142582cd3bb5cc29a2bc7181a6e67367b90b19f6a957ce506dcd7d1500bf95"
|
||||
name = "github.com/kolo/xmlrpc"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "16bdd962781df9696f40cc2bab924f1a855a7f89"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:417193ba917954c4837c6fc48c6ac241b3fefd13fc0889367b4a7e43b69d582c"
|
||||
name = "github.com/ldez/go-auroradns"
|
||||
|
@ -297,6 +313,14 @@
|
|||
revision = "ae18d6b8b3205b561c79e8e5f69bff09736185f4"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:a45ae66dea4c899d79fceb116accfa1892105c251f0dcd9a217ddc276b42ec68"
|
||||
name = "github.com/mitchellh/mapstructure"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe"
|
||||
version = "v1.1.2"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:2f42fa12d6911c7b7659738758631bec870b7e9b4c6be5444f963cdcfccc191f"
|
||||
name = "github.com/modern-go/concurrent"
|
||||
|
@ -382,6 +406,14 @@
|
|||
revision = "3e01752db0189b9157070a0e1668a620f9a85da2"
|
||||
version = "v1.0.6"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:94fcec8ba983a96bb3f123ab9690955baf44d6d32de342f95f5a2f665c70457a"
|
||||
name = "github.com/smueller18/goinwx"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "5d138389109eca96463f44f692408f0d1c731278"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:60a46e2410edbf02b419f833372dd1d24d7aa1b916a990a7370e792fada1eadd"
|
||||
name = "github.com/stretchr/objx"
|
||||
|
@ -620,6 +652,7 @@
|
|||
"github.com/rainycape/memcache",
|
||||
"github.com/sacloud/libsacloud/api",
|
||||
"github.com/sacloud/libsacloud/sacloud",
|
||||
"github.com/smueller18/goinwx",
|
||||
"github.com/stretchr/testify/assert",
|
||||
"github.com/stretchr/testify/mock",
|
||||
"github.com/stretchr/testify/require",
|
||||
|
|
|
@ -53,6 +53,10 @@
|
|||
branch = "master"
|
||||
name = "github.com/timewasted/linode"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/smueller18/goinwx"
|
||||
|
||||
[[constraint]]
|
||||
version = "0.5.1"
|
||||
name = "github.com/linode/linodego"
|
||||
|
|
2
cli.go
2
cli.go
|
@ -227,6 +227,7 @@ Here is an example bash command using the CloudFlare DNS provider:
|
|||
fmt.Fprintln(w, "\tgodaddy:\tGODADDY_API_KEY, GODADDY_API_SECRET")
|
||||
fmt.Fprintln(w, "\thostingde:\tHOSTINGDE_API_KEY, HOSTINGDE_ZONE_NAME")
|
||||
fmt.Fprintln(w, "\tiij:\tIIJ_API_ACCESS_KEY, IIJ_API_SECRET_KEY, IIJ_DO_SERVICE_CODE")
|
||||
fmt.Fprintln(w, "\tinwx:\tINWX_USERNAME, INWX_PASSWORD")
|
||||
fmt.Fprintln(w, "\tlightsail:\tAWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, DNS_ZONE")
|
||||
fmt.Fprintln(w, "\tlinode:\tLINODE_API_KEY")
|
||||
fmt.Fprintln(w, "\tlinodev4:\tLINODE_TOKEN")
|
||||
|
@ -274,6 +275,7 @@ Here is an example bash command using the CloudFlare DNS provider:
|
|||
fmt.Fprintln(w, "\tgodaddy:\tGODADDY_POLLING_INTERVAL, GODADDY_PROPAGATION_TIMEOUT, GODADDY_TTL, GODADDY_HTTP_TIMEOUT")
|
||||
fmt.Fprintln(w, "\thostingde:\tHOSTINGDE_POLLING_INTERVAL, HOSTINGDE_PROPAGATION_TIMEOUT, HOSTINGDE_TTL, HOSTINGDE_HTTP_TIMEOUT")
|
||||
fmt.Fprintln(w, "\tiij:\tIIJ_POLLING_INTERVAL, IIJ_PROPAGATION_TIMEOUT, IIJ_TTL")
|
||||
fmt.Fprintln(w, "\tinwx:\tINWX_POLLING_INTERVAL, INWX_PROPAGATION_TIMEOUT, INWX_TTL, INWX_SANDBOX")
|
||||
fmt.Fprintln(w, "\tlightsail:\tLIGHTSAIL_POLLING_INTERVAL, LIGHTSAIL_PROPAGATION_TIMEOUT")
|
||||
fmt.Fprintln(w, "\tlinode:\tLINODE_POLLING_INTERVAL, LINODE_TTL, LINODE_HTTP_TIMEOUT")
|
||||
fmt.Fprintln(w, "\tlinodev4:\tLINODE_POLLING_INTERVAL, LINODE_TTL, LINODE_HTTP_TIMEOUT")
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/xenolf/lego/providers/dns/godaddy"
|
||||
"github.com/xenolf/lego/providers/dns/hostingde"
|
||||
"github.com/xenolf/lego/providers/dns/iij"
|
||||
"github.com/xenolf/lego/providers/dns/inwx"
|
||||
"github.com/xenolf/lego/providers/dns/lightsail"
|
||||
"github.com/xenolf/lego/providers/dns/linode"
|
||||
"github.com/xenolf/lego/providers/dns/linodev4"
|
||||
|
@ -104,6 +105,8 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error)
|
|||
return hostingde.NewDNSProvider()
|
||||
case "iij":
|
||||
return iij.NewDNSProvider()
|
||||
case "inwx":
|
||||
return inwx.NewDNSProvider()
|
||||
case "lightsail":
|
||||
return lightsail.NewDNSProvider()
|
||||
case "linode":
|
||||
|
|
166
providers/dns/inwx/inwx.go
Normal file
166
providers/dns/inwx/inwx.go
Normal file
|
@ -0,0 +1,166 @@
|
|||
// Package inwx implements a DNS provider for solving the DNS-01 challenge using inwx dom robot
|
||||
package inwx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/smueller18/goinwx"
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/log"
|
||||
"github.com/xenolf/lego/platform/config/env"
|
||||
)
|
||||
|
||||
// Config is used to configure the creation of the DNSProvider
|
||||
type Config struct {
|
||||
Username string
|
||||
Password string
|
||||
Sandbox bool
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int
|
||||
}
|
||||
|
||||
// NewDefaultConfig returns a default configuration for the DNSProvider
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
PropagationTimeout: env.GetOrDefaultSecond("INWX_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout),
|
||||
PollingInterval: env.GetOrDefaultSecond("INWX_POLLING_INTERVAL", acme.DefaultPollingInterval),
|
||||
TTL: env.GetOrDefaultInt("INWX_TTL", 300),
|
||||
Sandbox: env.GetOrDefaultBool("INWX_SANDBOX", false),
|
||||
}
|
||||
}
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface
|
||||
type DNSProvider struct {
|
||||
config *Config
|
||||
client *goinwx.Client
|
||||
}
|
||||
|
||||
// NewDNSProvider returns a DNSProvider instance configured for Dyn DNS.
|
||||
// Credentials must be passed in the environment variables:
|
||||
// INWX_USERNAME and INWX_PASSWORD.
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get("INWX_USERNAME", "INWX_PASSWORD")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("inwx: %v", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.Username = values["INWX_USERNAME"]
|
||||
config.Password = values["INWX_PASSWORD"]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
// NewDNSProviderConfig return a DNSProvider instance configured for Dyn DNS
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("inwx: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
if config.Username == "" || config.Password == "" {
|
||||
return nil, fmt.Errorf("inwx: credentials missing")
|
||||
}
|
||||
|
||||
if config.Sandbox {
|
||||
log.Infof("inwx: sandbox mode is enabled")
|
||||
}
|
||||
|
||||
client := goinwx.NewClient(config.Username, config.Password, &goinwx.ClientOptions{Sandbox: config.Sandbox})
|
||||
|
||||
return &DNSProvider{config: config, client: client}, nil
|
||||
}
|
||||
|
||||
// Present creates a TXT record using the specified parameters
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inwx: %v", err)
|
||||
}
|
||||
|
||||
err = d.client.Account.Login()
|
||||
if err != nil {
|
||||
return fmt.Errorf("inwx: %v", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
errL := d.client.Account.Logout()
|
||||
if errL != nil {
|
||||
log.Infof("inwx: failed to logout: %v", errL)
|
||||
}
|
||||
}()
|
||||
|
||||
var request = &goinwx.NameserverRecordRequest{
|
||||
Domain: acme.UnFqdn(authZone),
|
||||
Name: acme.UnFqdn(fqdn),
|
||||
Type: "TXT",
|
||||
Content: value,
|
||||
Ttl: d.config.TTL,
|
||||
}
|
||||
|
||||
_, err = d.client.Nameservers.CreateRecord(request)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case *goinwx.ErrorResponse:
|
||||
if err.(*goinwx.ErrorResponse).Message == "Object exists" {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("inwx: %v", err)
|
||||
default:
|
||||
return fmt.Errorf("inwx: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp removes the TXT record matching the specified parameters
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inwx: %v", err)
|
||||
}
|
||||
|
||||
err = d.client.Account.Login()
|
||||
if err != nil {
|
||||
return fmt.Errorf("inwx: %v", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
errL := d.client.Account.Logout()
|
||||
if errL != nil {
|
||||
log.Infof("inwx: failed to logout: %v", errL)
|
||||
}
|
||||
}()
|
||||
|
||||
response, err := d.client.Nameservers.Info(&goinwx.NameserverInfoRequest{
|
||||
Domain: acme.UnFqdn(authZone),
|
||||
Name: acme.UnFqdn(fqdn),
|
||||
Type: "TXT",
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("inwx: %v", err)
|
||||
}
|
||||
|
||||
var lastErr error
|
||||
for _, record := range response.Records {
|
||||
err = d.client.Nameservers.DeleteRecord(record.Id)
|
||||
if err != nil {
|
||||
lastErr = fmt.Errorf("inwx: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return lastErr
|
||||
}
|
||||
|
||||
// Timeout returns the timeout and interval to use when checking for DNS propagation.
|
||||
// Adjusting here to cope with spikes in propagation times.
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
140
providers/dns/inwx/inwx_test.go
Normal file
140
providers/dns/inwx/inwx_test.go
Normal file
|
@ -0,0 +1,140 @@
|
|||
package inwx
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/xenolf/lego/platform/tester"
|
||||
)
|
||||
|
||||
var envTest = tester.NewEnvTest(
|
||||
"INWX_USERNAME",
|
||||
"INWX_PASSWORD",
|
||||
"INWX_SANDBOX",
|
||||
"INWX_TTL").
|
||||
WithDomain("INWX_DOMAIN").
|
||||
WithLiveTestRequirements("INWX_USERNAME", "INWX_PASSWORD", "INWX_DOMAIN")
|
||||
|
||||
func TestNewDNSProvider(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
envVars map[string]string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "success",
|
||||
envVars: map[string]string{
|
||||
"INWX_USERNAME": "123",
|
||||
"INWX_PASSWORD": "456",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "missing credentials",
|
||||
envVars: map[string]string{
|
||||
"INWX_USERNAME": "",
|
||||
"INWX_PASSWORD": "",
|
||||
},
|
||||
expected: "inwx: some credentials information are missing: INWX_USERNAME,INWX_PASSWORD",
|
||||
},
|
||||
{
|
||||
desc: "missing username",
|
||||
envVars: map[string]string{
|
||||
"INWX_USERNAME": "",
|
||||
"INWX_PASSWORD": "456",
|
||||
},
|
||||
expected: "inwx: some credentials information are missing: INWX_USERNAME",
|
||||
},
|
||||
{
|
||||
desc: "missing password",
|
||||
envVars: map[string]string{
|
||||
"INWX_USERNAME": "123",
|
||||
"INWX_PASSWORD": "",
|
||||
},
|
||||
expected: "inwx: some credentials information are missing: INWX_PASSWORD",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
defer envTest.RestoreEnv()
|
||||
envTest.ClearEnv()
|
||||
|
||||
envTest.Apply(test.envVars)
|
||||
|
||||
p, err := NewDNSProvider()
|
||||
|
||||
if len(test.expected) == 0 {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, p)
|
||||
require.NotNil(t, p.config)
|
||||
require.NotNil(t, p.client)
|
||||
} else {
|
||||
require.EqualError(t, err, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewDNSProviderConfig(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
username string
|
||||
password string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "success",
|
||||
username: "123",
|
||||
password: "456",
|
||||
},
|
||||
{
|
||||
desc: "missing credentials",
|
||||
expected: "inwx: credentials missing",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
config := NewDefaultConfig()
|
||||
config.Username = test.username
|
||||
config.Password = test.password
|
||||
|
||||
p, err := NewDNSProviderConfig(config)
|
||||
|
||||
if len(test.expected) == 0 {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, p)
|
||||
require.NotNil(t, p.config)
|
||||
require.NotNil(t, p.client)
|
||||
} else {
|
||||
require.EqualError(t, err, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLivePresentAndCleanup(t *testing.T) {
|
||||
if !envTest.IsLiveTest() {
|
||||
t.Skip("skipping live test")
|
||||
}
|
||||
|
||||
envTest.RestoreEnv()
|
||||
envTest.Apply(map[string]string{
|
||||
"INWX_SANDBOX": "true",
|
||||
"INWX_TTL": "3600", // In sandbox mode, the minimum allowed TTL is 3600
|
||||
})
|
||||
defer envTest.RestoreEnv()
|
||||
|
||||
provider, err := NewDNSProvider()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = provider.Present(envTest.GetDomain(), "", "123d==")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify that no error is thrown if record already exists
|
||||
err = provider.Present(envTest.GetDomain(), "", "123d==")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
|
||||
require.NoError(t, err)
|
||||
}
|
21
vendor/github.com/fatih/structs/LICENSE
generated
vendored
Normal file
21
vendor/github.com/fatih/structs/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Fatih Arslan
|
||||
|
||||
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.
|
141
vendor/github.com/fatih/structs/field.go
generated
vendored
Normal file
141
vendor/github.com/fatih/structs/field.go
generated
vendored
Normal file
|
@ -0,0 +1,141 @@
|
|||
package structs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var (
|
||||
errNotExported = errors.New("field is not exported")
|
||||
errNotSettable = errors.New("field is not settable")
|
||||
)
|
||||
|
||||
// Field represents a single struct field that encapsulates high level
|
||||
// functions around the field.
|
||||
type Field struct {
|
||||
value reflect.Value
|
||||
field reflect.StructField
|
||||
defaultTag string
|
||||
}
|
||||
|
||||
// Tag returns the value associated with key in the tag string. If there is no
|
||||
// such key in the tag, Tag returns the empty string.
|
||||
func (f *Field) Tag(key string) string {
|
||||
return f.field.Tag.Get(key)
|
||||
}
|
||||
|
||||
// Value returns the underlying value of the field. It panics if the field
|
||||
// is not exported.
|
||||
func (f *Field) Value() interface{} {
|
||||
return f.value.Interface()
|
||||
}
|
||||
|
||||
// IsEmbedded returns true if the given field is an anonymous field (embedded)
|
||||
func (f *Field) IsEmbedded() bool {
|
||||
return f.field.Anonymous
|
||||
}
|
||||
|
||||
// IsExported returns true if the given field is exported.
|
||||
func (f *Field) IsExported() bool {
|
||||
return f.field.PkgPath == ""
|
||||
}
|
||||
|
||||
// IsZero returns true if the given field is not initialized (has a zero value).
|
||||
// It panics if the field is not exported.
|
||||
func (f *Field) IsZero() bool {
|
||||
zero := reflect.Zero(f.value.Type()).Interface()
|
||||
current := f.Value()
|
||||
|
||||
return reflect.DeepEqual(current, zero)
|
||||
}
|
||||
|
||||
// Name returns the name of the given field
|
||||
func (f *Field) Name() string {
|
||||
return f.field.Name
|
||||
}
|
||||
|
||||
// Kind returns the fields kind, such as "string", "map", "bool", etc ..
|
||||
func (f *Field) Kind() reflect.Kind {
|
||||
return f.value.Kind()
|
||||
}
|
||||
|
||||
// Set sets the field to given value v. It returns an error if the field is not
|
||||
// settable (not addressable or not exported) or if the given value's type
|
||||
// doesn't match the fields type.
|
||||
func (f *Field) Set(val interface{}) error {
|
||||
// we can't set unexported fields, so be sure this field is exported
|
||||
if !f.IsExported() {
|
||||
return errNotExported
|
||||
}
|
||||
|
||||
// do we get here? not sure...
|
||||
if !f.value.CanSet() {
|
||||
return errNotSettable
|
||||
}
|
||||
|
||||
given := reflect.ValueOf(val)
|
||||
|
||||
if f.value.Kind() != given.Kind() {
|
||||
return fmt.Errorf("wrong kind. got: %s want: %s", given.Kind(), f.value.Kind())
|
||||
}
|
||||
|
||||
f.value.Set(given)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Zero sets the field to its zero value. It returns an error if the field is not
|
||||
// settable (not addressable or not exported).
|
||||
func (f *Field) Zero() error {
|
||||
zero := reflect.Zero(f.value.Type()).Interface()
|
||||
return f.Set(zero)
|
||||
}
|
||||
|
||||
// Fields returns a slice of Fields. This is particular handy to get the fields
|
||||
// of a nested struct . A struct tag with the content of "-" ignores the
|
||||
// checking of that particular field. Example:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field *http.Request `structs:"-"`
|
||||
//
|
||||
// It panics if field is not exported or if field's kind is not struct
|
||||
func (f *Field) Fields() []*Field {
|
||||
return getFields(f.value, f.defaultTag)
|
||||
}
|
||||
|
||||
// Field returns the field from a nested struct. It panics if the nested struct
|
||||
// is not exported or if the field was not found.
|
||||
func (f *Field) Field(name string) *Field {
|
||||
field, ok := f.FieldOk(name)
|
||||
if !ok {
|
||||
panic("field not found")
|
||||
}
|
||||
|
||||
return field
|
||||
}
|
||||
|
||||
// FieldOk returns the field from a nested struct. The boolean returns whether
|
||||
// the field was found (true) or not (false).
|
||||
func (f *Field) FieldOk(name string) (*Field, bool) {
|
||||
value := &f.value
|
||||
// value must be settable so we need to make sure it holds the address of the
|
||||
// variable and not a copy, so we can pass the pointer to strctVal instead of a
|
||||
// copy (which is not assigned to any variable, hence not settable).
|
||||
// see "https://blog.golang.org/laws-of-reflection#TOC_8."
|
||||
if f.value.Kind() != reflect.Ptr {
|
||||
a := f.value.Addr()
|
||||
value = &a
|
||||
}
|
||||
v := strctVal(value.Interface())
|
||||
t := v.Type()
|
||||
|
||||
field, ok := t.FieldByName(name)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return &Field{
|
||||
field: field,
|
||||
value: v.FieldByName(name),
|
||||
}, true
|
||||
}
|
584
vendor/github.com/fatih/structs/structs.go
generated
vendored
Normal file
584
vendor/github.com/fatih/structs/structs.go
generated
vendored
Normal file
|
@ -0,0 +1,584 @@
|
|||
// Package structs contains various utilities functions to work with structs.
|
||||
package structs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultTagName is the default tag name for struct fields which provides
|
||||
// a more granular to tweak certain structs. Lookup the necessary functions
|
||||
// for more info.
|
||||
DefaultTagName = "structs" // struct's field default tag name
|
||||
)
|
||||
|
||||
// Struct encapsulates a struct type to provide several high level functions
|
||||
// around the struct.
|
||||
type Struct struct {
|
||||
raw interface{}
|
||||
value reflect.Value
|
||||
TagName string
|
||||
}
|
||||
|
||||
// New returns a new *Struct with the struct s. It panics if the s's kind is
|
||||
// not struct.
|
||||
func New(s interface{}) *Struct {
|
||||
return &Struct{
|
||||
raw: s,
|
||||
value: strctVal(s),
|
||||
TagName: DefaultTagName,
|
||||
}
|
||||
}
|
||||
|
||||
// Map converts the given struct to a map[string]interface{}, where the keys
|
||||
// of the map are the field names and the values of the map the associated
|
||||
// values of the fields. The default key string is the struct field name but
|
||||
// can be changed in the struct field's tag value. The "structs" key in the
|
||||
// struct's field tag value is the key name. Example:
|
||||
//
|
||||
// // Field appears in map as key "myName".
|
||||
// Name string `structs:"myName"`
|
||||
//
|
||||
// A tag value with the content of "-" ignores that particular field. Example:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field bool `structs:"-"`
|
||||
//
|
||||
// A tag value with the content of "string" uses the stringer to get the value. Example:
|
||||
//
|
||||
// // The value will be output of Animal's String() func.
|
||||
// // Map will panic if Animal does not implement String().
|
||||
// Field *Animal `structs:"field,string"`
|
||||
//
|
||||
// A tag value with the option of "flatten" used in a struct field is to flatten its fields
|
||||
// in the output map. Example:
|
||||
//
|
||||
// // The FieldStruct's fields will be flattened into the output map.
|
||||
// FieldStruct time.Time `structs:",flatten"`
|
||||
//
|
||||
// A tag value with the option of "omitnested" stops iterating further if the type
|
||||
// is a struct. Example:
|
||||
//
|
||||
// // Field is not processed further by this package.
|
||||
// Field time.Time `structs:"myName,omitnested"`
|
||||
// Field *http.Request `structs:",omitnested"`
|
||||
//
|
||||
// A tag value with the option of "omitempty" ignores that particular field if
|
||||
// the field value is empty. Example:
|
||||
//
|
||||
// // Field appears in map as key "myName", but the field is
|
||||
// // skipped if empty.
|
||||
// Field string `structs:"myName,omitempty"`
|
||||
//
|
||||
// // Field appears in map as key "Field" (the default), but
|
||||
// // the field is skipped if empty.
|
||||
// Field string `structs:",omitempty"`
|
||||
//
|
||||
// Note that only exported fields of a struct can be accessed, non exported
|
||||
// fields will be neglected.
|
||||
func (s *Struct) Map() map[string]interface{} {
|
||||
out := make(map[string]interface{})
|
||||
s.FillMap(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// FillMap is the same as Map. Instead of returning the output, it fills the
|
||||
// given map.
|
||||
func (s *Struct) FillMap(out map[string]interface{}) {
|
||||
if out == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fields := s.structFields()
|
||||
|
||||
for _, field := range fields {
|
||||
name := field.Name
|
||||
val := s.value.FieldByName(name)
|
||||
isSubStruct := false
|
||||
var finalVal interface{}
|
||||
|
||||
tagName, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
||||
if tagName != "" {
|
||||
name = tagName
|
||||
}
|
||||
|
||||
// if the value is a zero value and the field is marked as omitempty do
|
||||
// not include
|
||||
if tagOpts.Has("omitempty") {
|
||||
zero := reflect.Zero(val.Type()).Interface()
|
||||
current := val.Interface()
|
||||
|
||||
if reflect.DeepEqual(current, zero) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if !tagOpts.Has("omitnested") {
|
||||
finalVal = s.nested(val)
|
||||
|
||||
v := reflect.ValueOf(val.Interface())
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Map, reflect.Struct:
|
||||
isSubStruct = true
|
||||
}
|
||||
} else {
|
||||
finalVal = val.Interface()
|
||||
}
|
||||
|
||||
if tagOpts.Has("string") {
|
||||
s, ok := val.Interface().(fmt.Stringer)
|
||||
if ok {
|
||||
out[name] = s.String()
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if isSubStruct && (tagOpts.Has("flatten")) {
|
||||
for k := range finalVal.(map[string]interface{}) {
|
||||
out[k] = finalVal.(map[string]interface{})[k]
|
||||
}
|
||||
} else {
|
||||
out[name] = finalVal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Values converts the given s struct's field values to a []interface{}. A
|
||||
// struct tag with the content of "-" ignores the that particular field.
|
||||
// Example:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field int `structs:"-"`
|
||||
//
|
||||
// A value with the option of "omitnested" stops iterating further if the type
|
||||
// is a struct. Example:
|
||||
//
|
||||
// // Fields is not processed further by this package.
|
||||
// Field time.Time `structs:",omitnested"`
|
||||
// Field *http.Request `structs:",omitnested"`
|
||||
//
|
||||
// A tag value with the option of "omitempty" ignores that particular field and
|
||||
// is not added to the values if the field value is empty. Example:
|
||||
//
|
||||
// // Field is skipped if empty
|
||||
// Field string `structs:",omitempty"`
|
||||
//
|
||||
// Note that only exported fields of a struct can be accessed, non exported
|
||||
// fields will be neglected.
|
||||
func (s *Struct) Values() []interface{} {
|
||||
fields := s.structFields()
|
||||
|
||||
var t []interface{}
|
||||
|
||||
for _, field := range fields {
|
||||
val := s.value.FieldByName(field.Name)
|
||||
|
||||
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
||||
|
||||
// if the value is a zero value and the field is marked as omitempty do
|
||||
// not include
|
||||
if tagOpts.Has("omitempty") {
|
||||
zero := reflect.Zero(val.Type()).Interface()
|
||||
current := val.Interface()
|
||||
|
||||
if reflect.DeepEqual(current, zero) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if tagOpts.Has("string") {
|
||||
s, ok := val.Interface().(fmt.Stringer)
|
||||
if ok {
|
||||
t = append(t, s.String())
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
||||
// look out for embedded structs, and convert them to a
|
||||
// []interface{} to be added to the final values slice
|
||||
t = append(t, Values(val.Interface())...)
|
||||
} else {
|
||||
t = append(t, val.Interface())
|
||||
}
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// Fields returns a slice of Fields. A struct tag with the content of "-"
|
||||
// ignores the checking of that particular field. Example:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field bool `structs:"-"`
|
||||
//
|
||||
// It panics if s's kind is not struct.
|
||||
func (s *Struct) Fields() []*Field {
|
||||
return getFields(s.value, s.TagName)
|
||||
}
|
||||
|
||||
// Names returns a slice of field names. A struct tag with the content of "-"
|
||||
// ignores the checking of that particular field. Example:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field bool `structs:"-"`
|
||||
//
|
||||
// It panics if s's kind is not struct.
|
||||
func (s *Struct) Names() []string {
|
||||
fields := getFields(s.value, s.TagName)
|
||||
|
||||
names := make([]string, len(fields))
|
||||
|
||||
for i, field := range fields {
|
||||
names[i] = field.Name()
|
||||
}
|
||||
|
||||
return names
|
||||
}
|
||||
|
||||
func getFields(v reflect.Value, tagName string) []*Field {
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
t := v.Type()
|
||||
|
||||
var fields []*Field
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
|
||||
if tag := field.Tag.Get(tagName); tag == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
f := &Field{
|
||||
field: field,
|
||||
value: v.FieldByName(field.Name),
|
||||
}
|
||||
|
||||
fields = append(fields, f)
|
||||
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
// Field returns a new Field struct that provides several high level functions
|
||||
// around a single struct field entity. It panics if the field is not found.
|
||||
func (s *Struct) Field(name string) *Field {
|
||||
f, ok := s.FieldOk(name)
|
||||
if !ok {
|
||||
panic("field not found")
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// FieldOk returns a new Field struct that provides several high level functions
|
||||
// around a single struct field entity. The boolean returns true if the field
|
||||
// was found.
|
||||
func (s *Struct) FieldOk(name string) (*Field, bool) {
|
||||
t := s.value.Type()
|
||||
|
||||
field, ok := t.FieldByName(name)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return &Field{
|
||||
field: field,
|
||||
value: s.value.FieldByName(name),
|
||||
defaultTag: s.TagName,
|
||||
}, true
|
||||
}
|
||||
|
||||
// IsZero returns true if all fields in a struct is a zero value (not
|
||||
// initialized) A struct tag with the content of "-" ignores the checking of
|
||||
// that particular field. Example:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field bool `structs:"-"`
|
||||
//
|
||||
// A value with the option of "omitnested" stops iterating further if the type
|
||||
// is a struct. Example:
|
||||
//
|
||||
// // Field is not processed further by this package.
|
||||
// Field time.Time `structs:"myName,omitnested"`
|
||||
// Field *http.Request `structs:",omitnested"`
|
||||
//
|
||||
// Note that only exported fields of a struct can be accessed, non exported
|
||||
// fields will be neglected. It panics if s's kind is not struct.
|
||||
func (s *Struct) IsZero() bool {
|
||||
fields := s.structFields()
|
||||
|
||||
for _, field := range fields {
|
||||
val := s.value.FieldByName(field.Name)
|
||||
|
||||
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
||||
|
||||
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
||||
ok := IsZero(val.Interface())
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// zero value of the given field, such as "" for string, 0 for int
|
||||
zero := reflect.Zero(val.Type()).Interface()
|
||||
|
||||
// current value of the given field
|
||||
current := val.Interface()
|
||||
|
||||
if !reflect.DeepEqual(current, zero) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// HasZero returns true if a field in a struct is not initialized (zero value).
|
||||
// A struct tag with the content of "-" ignores the checking of that particular
|
||||
// field. Example:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field bool `structs:"-"`
|
||||
//
|
||||
// A value with the option of "omitnested" stops iterating further if the type
|
||||
// is a struct. Example:
|
||||
//
|
||||
// // Field is not processed further by this package.
|
||||
// Field time.Time `structs:"myName,omitnested"`
|
||||
// Field *http.Request `structs:",omitnested"`
|
||||
//
|
||||
// Note that only exported fields of a struct can be accessed, non exported
|
||||
// fields will be neglected. It panics if s's kind is not struct.
|
||||
func (s *Struct) HasZero() bool {
|
||||
fields := s.structFields()
|
||||
|
||||
for _, field := range fields {
|
||||
val := s.value.FieldByName(field.Name)
|
||||
|
||||
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
||||
|
||||
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
||||
ok := HasZero(val.Interface())
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// zero value of the given field, such as "" for string, 0 for int
|
||||
zero := reflect.Zero(val.Type()).Interface()
|
||||
|
||||
// current value of the given field
|
||||
current := val.Interface()
|
||||
|
||||
if reflect.DeepEqual(current, zero) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Name returns the structs's type name within its package. For more info refer
|
||||
// to Name() function.
|
||||
func (s *Struct) Name() string {
|
||||
return s.value.Type().Name()
|
||||
}
|
||||
|
||||
// structFields returns the exported struct fields for a given s struct. This
|
||||
// is a convenient helper method to avoid duplicate code in some of the
|
||||
// functions.
|
||||
func (s *Struct) structFields() []reflect.StructField {
|
||||
t := s.value.Type()
|
||||
|
||||
var f []reflect.StructField
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
// we can't access the value of unexported fields
|
||||
if field.PkgPath != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// don't check if it's omitted
|
||||
if tag := field.Tag.Get(s.TagName); tag == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
f = append(f, field)
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func strctVal(s interface{}) reflect.Value {
|
||||
v := reflect.ValueOf(s)
|
||||
|
||||
// if pointer get the underlying element≤
|
||||
for v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
if v.Kind() != reflect.Struct {
|
||||
panic("not struct")
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Map converts the given struct to a map[string]interface{}. For more info
|
||||
// refer to Struct types Map() method. It panics if s's kind is not struct.
|
||||
func Map(s interface{}) map[string]interface{} {
|
||||
return New(s).Map()
|
||||
}
|
||||
|
||||
// FillMap is the same as Map. Instead of returning the output, it fills the
|
||||
// given map.
|
||||
func FillMap(s interface{}, out map[string]interface{}) {
|
||||
New(s).FillMap(out)
|
||||
}
|
||||
|
||||
// Values converts the given struct to a []interface{}. For more info refer to
|
||||
// Struct types Values() method. It panics if s's kind is not struct.
|
||||
func Values(s interface{}) []interface{} {
|
||||
return New(s).Values()
|
||||
}
|
||||
|
||||
// Fields returns a slice of *Field. For more info refer to Struct types
|
||||
// Fields() method. It panics if s's kind is not struct.
|
||||
func Fields(s interface{}) []*Field {
|
||||
return New(s).Fields()
|
||||
}
|
||||
|
||||
// Names returns a slice of field names. For more info refer to Struct types
|
||||
// Names() method. It panics if s's kind is not struct.
|
||||
func Names(s interface{}) []string {
|
||||
return New(s).Names()
|
||||
}
|
||||
|
||||
// IsZero returns true if all fields is equal to a zero value. For more info
|
||||
// refer to Struct types IsZero() method. It panics if s's kind is not struct.
|
||||
func IsZero(s interface{}) bool {
|
||||
return New(s).IsZero()
|
||||
}
|
||||
|
||||
// HasZero returns true if any field is equal to a zero value. For more info
|
||||
// refer to Struct types HasZero() method. It panics if s's kind is not struct.
|
||||
func HasZero(s interface{}) bool {
|
||||
return New(s).HasZero()
|
||||
}
|
||||
|
||||
// IsStruct returns true if the given variable is a struct or a pointer to
|
||||
// struct.
|
||||
func IsStruct(s interface{}) bool {
|
||||
v := reflect.ValueOf(s)
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
// uninitialized zero value of a struct
|
||||
if v.Kind() == reflect.Invalid {
|
||||
return false
|
||||
}
|
||||
|
||||
return v.Kind() == reflect.Struct
|
||||
}
|
||||
|
||||
// Name returns the structs's type name within its package. It returns an
|
||||
// empty string for unnamed types. It panics if s's kind is not struct.
|
||||
func Name(s interface{}) string {
|
||||
return New(s).Name()
|
||||
}
|
||||
|
||||
// nested retrieves recursively all types for the given value and returns the
|
||||
// nested value.
|
||||
func (s *Struct) nested(val reflect.Value) interface{} {
|
||||
var finalVal interface{}
|
||||
|
||||
v := reflect.ValueOf(val.Interface())
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Struct:
|
||||
n := New(val.Interface())
|
||||
n.TagName = s.TagName
|
||||
m := n.Map()
|
||||
|
||||
// do not add the converted value if there are no exported fields, ie:
|
||||
// time.Time
|
||||
if len(m) == 0 {
|
||||
finalVal = val.Interface()
|
||||
} else {
|
||||
finalVal = m
|
||||
}
|
||||
case reflect.Map:
|
||||
// get the element type of the map
|
||||
mapElem := val.Type()
|
||||
switch val.Type().Kind() {
|
||||
case reflect.Ptr, reflect.Array, reflect.Map,
|
||||
reflect.Slice, reflect.Chan:
|
||||
mapElem = val.Type().Elem()
|
||||
if mapElem.Kind() == reflect.Ptr {
|
||||
mapElem = mapElem.Elem()
|
||||
}
|
||||
}
|
||||
|
||||
// only iterate over struct types, ie: map[string]StructType,
|
||||
// map[string][]StructType,
|
||||
if mapElem.Kind() == reflect.Struct ||
|
||||
(mapElem.Kind() == reflect.Slice &&
|
||||
mapElem.Elem().Kind() == reflect.Struct) {
|
||||
m := make(map[string]interface{}, val.Len())
|
||||
for _, k := range val.MapKeys() {
|
||||
m[k.String()] = s.nested(val.MapIndex(k))
|
||||
}
|
||||
finalVal = m
|
||||
break
|
||||
}
|
||||
|
||||
// TODO(arslan): should this be optional?
|
||||
finalVal = val.Interface()
|
||||
case reflect.Slice, reflect.Array:
|
||||
if val.Type().Kind() == reflect.Interface {
|
||||
finalVal = val.Interface()
|
||||
break
|
||||
}
|
||||
|
||||
// TODO(arslan): should this be optional?
|
||||
// do not iterate of non struct types, just pass the value. Ie: []int,
|
||||
// []string, co... We only iterate further if it's a struct.
|
||||
// i.e []foo or []*foo
|
||||
if val.Type().Elem().Kind() != reflect.Struct &&
|
||||
!(val.Type().Elem().Kind() == reflect.Ptr &&
|
||||
val.Type().Elem().Elem().Kind() == reflect.Struct) {
|
||||
finalVal = val.Interface()
|
||||
break
|
||||
}
|
||||
|
||||
slices := make([]interface{}, val.Len())
|
||||
for x := 0; x < val.Len(); x++ {
|
||||
slices[x] = s.nested(val.Index(x))
|
||||
}
|
||||
finalVal = slices
|
||||
default:
|
||||
finalVal = val.Interface()
|
||||
}
|
||||
|
||||
return finalVal
|
||||
}
|
32
vendor/github.com/fatih/structs/tags.go
generated
vendored
Normal file
32
vendor/github.com/fatih/structs/tags.go
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
package structs
|
||||
|
||||
import "strings"
|
||||
|
||||
// tagOptions contains a slice of tag options
|
||||
type tagOptions []string
|
||||
|
||||
// Has returns true if the given option is available in tagOptions
|
||||
func (t tagOptions) Has(opt string) bool {
|
||||
for _, tagOpt := range t {
|
||||
if tagOpt == opt {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// parseTag splits a struct field's tag into its name and a list of options
|
||||
// which comes after a name. A tag is in the form of: "name,option1,option2".
|
||||
// The name can be neglectected.
|
||||
func parseTag(tag string) (string, tagOptions) {
|
||||
// tag is one of followings:
|
||||
// ""
|
||||
// "name"
|
||||
// "name,opt"
|
||||
// "name,opt,opt2"
|
||||
// ",opt"
|
||||
|
||||
res := strings.Split(tag, ",")
|
||||
return res[0], res[1:]
|
||||
}
|
19
vendor/github.com/kolo/xmlrpc/LICENSE
generated
vendored
Normal file
19
vendor/github.com/kolo/xmlrpc/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
Copyright (C) 2012 Dmitry Maksimov
|
||||
|
||||
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.
|
169
vendor/github.com/kolo/xmlrpc/client.go
generated
vendored
Normal file
169
vendor/github.com/kolo/xmlrpc/client.go
generated
vendored
Normal file
|
@ -0,0 +1,169 @@
|
|||
package xmlrpc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/rpc"
|
||||
"net/url"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
*rpc.Client
|
||||
}
|
||||
|
||||
// clientCodec is rpc.ClientCodec interface implementation.
|
||||
type clientCodec struct {
|
||||
// url presents url of xmlrpc service
|
||||
url *url.URL
|
||||
|
||||
// httpClient works with HTTP protocol
|
||||
httpClient *http.Client
|
||||
|
||||
// cookies stores cookies received on last request
|
||||
cookies http.CookieJar
|
||||
|
||||
// responses presents map of active requests. It is required to return request id, that
|
||||
// rpc.Client can mark them as done.
|
||||
responses map[uint64]*http.Response
|
||||
mutex sync.Mutex
|
||||
|
||||
response *Response
|
||||
|
||||
// ready presents channel, that is used to link request and it`s response.
|
||||
ready chan uint64
|
||||
|
||||
// close notifies codec is closed.
|
||||
close chan uint64
|
||||
}
|
||||
|
||||
func (codec *clientCodec) WriteRequest(request *rpc.Request, args interface{}) (err error) {
|
||||
httpRequest, err := NewRequest(codec.url.String(), request.ServiceMethod, args)
|
||||
|
||||
if codec.cookies != nil {
|
||||
for _, cookie := range codec.cookies.Cookies(codec.url) {
|
||||
httpRequest.AddCookie(cookie)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var httpResponse *http.Response
|
||||
httpResponse, err = codec.httpClient.Do(httpRequest)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if codec.cookies != nil {
|
||||
codec.cookies.SetCookies(codec.url, httpResponse.Cookies())
|
||||
}
|
||||
|
||||
codec.mutex.Lock()
|
||||
codec.responses[request.Seq] = httpResponse
|
||||
codec.mutex.Unlock()
|
||||
|
||||
codec.ready <- request.Seq
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (codec *clientCodec) ReadResponseHeader(response *rpc.Response) (err error) {
|
||||
var seq uint64
|
||||
|
||||
select {
|
||||
case seq = <-codec.ready:
|
||||
case <-codec.close:
|
||||
return errors.New("codec is closed")
|
||||
}
|
||||
|
||||
codec.mutex.Lock()
|
||||
httpResponse := codec.responses[seq]
|
||||
codec.mutex.Unlock()
|
||||
|
||||
if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 {
|
||||
return fmt.Errorf("request error: bad status code - %d", httpResponse.StatusCode)
|
||||
}
|
||||
|
||||
respData, err := ioutil.ReadAll(httpResponse.Body)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
httpResponse.Body.Close()
|
||||
|
||||
resp := NewResponse(respData)
|
||||
|
||||
if resp.Failed() {
|
||||
response.Error = fmt.Sprintf("%v", resp.Err())
|
||||
}
|
||||
|
||||
codec.response = resp
|
||||
|
||||
response.Seq = seq
|
||||
|
||||
codec.mutex.Lock()
|
||||
delete(codec.responses, seq)
|
||||
codec.mutex.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (codec *clientCodec) ReadResponseBody(v interface{}) (err error) {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = codec.response.Unmarshal(v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (codec *clientCodec) Close() error {
|
||||
transport := codec.httpClient.Transport.(*http.Transport)
|
||||
transport.CloseIdleConnections()
|
||||
|
||||
close(codec.close)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewClient returns instance of rpc.Client object, that is used to send request to xmlrpc service.
|
||||
func NewClient(requrl string, transport http.RoundTripper) (*Client, error) {
|
||||
if transport == nil {
|
||||
transport = http.DefaultTransport
|
||||
}
|
||||
|
||||
httpClient := &http.Client{Transport: transport}
|
||||
|
||||
jar, err := cookiejar.New(nil)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u, err := url.Parse(requrl)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
codec := clientCodec{
|
||||
url: u,
|
||||
httpClient: httpClient,
|
||||
close: make(chan uint64),
|
||||
ready: make(chan uint64),
|
||||
responses: make(map[uint64]*http.Response),
|
||||
cookies: jar,
|
||||
}
|
||||
|
||||
return &Client{rpc.NewClientWithCodec(&codec)}, nil
|
||||
}
|
463
vendor/github.com/kolo/xmlrpc/decoder.go
generated
vendored
Normal file
463
vendor/github.com/kolo/xmlrpc/decoder.go
generated
vendored
Normal file
|
@ -0,0 +1,463 @@
|
|||
package xmlrpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
iso8601 = "20060102T15:04:05"
|
||||
iso8601Z = "20060102T15:04:05Z07:00"
|
||||
iso8601Hyphen = "2006-01-02T15:04:05"
|
||||
iso8601HyphenZ = "2006-01-02T15:04:05Z07:00"
|
||||
)
|
||||
|
||||
var (
|
||||
// CharsetReader is a function to generate reader which converts a non UTF-8
|
||||
// charset into UTF-8.
|
||||
CharsetReader func(string, io.Reader) (io.Reader, error)
|
||||
|
||||
timeLayouts = []string{iso8601, iso8601Z, iso8601Hyphen, iso8601HyphenZ}
|
||||
invalidXmlError = errors.New("invalid xml")
|
||||
)
|
||||
|
||||
type TypeMismatchError string
|
||||
|
||||
func (e TypeMismatchError) Error() string { return string(e) }
|
||||
|
||||
type decoder struct {
|
||||
*xml.Decoder
|
||||
}
|
||||
|
||||
func unmarshal(data []byte, v interface{}) (err error) {
|
||||
dec := &decoder{xml.NewDecoder(bytes.NewBuffer(data))}
|
||||
|
||||
if CharsetReader != nil {
|
||||
dec.CharsetReader = CharsetReader
|
||||
}
|
||||
|
||||
var tok xml.Token
|
||||
for {
|
||||
if tok, err = dec.Token(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if t, ok := tok.(xml.StartElement); ok {
|
||||
if t.Name.Local == "value" {
|
||||
val := reflect.ValueOf(v)
|
||||
if val.Kind() != reflect.Ptr {
|
||||
return errors.New("non-pointer value passed to unmarshal")
|
||||
}
|
||||
if err = dec.decodeValue(val.Elem()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// read until end of document
|
||||
err = dec.Skip()
|
||||
if err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dec *decoder) decodeValue(val reflect.Value) error {
|
||||
var tok xml.Token
|
||||
var err error
|
||||
|
||||
if val.Kind() == reflect.Ptr {
|
||||
if val.IsNil() {
|
||||
val.Set(reflect.New(val.Type().Elem()))
|
||||
}
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
var typeName string
|
||||
for {
|
||||
if tok, err = dec.Token(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if t, ok := tok.(xml.EndElement); ok {
|
||||
if t.Name.Local == "value" {
|
||||
return nil
|
||||
} else {
|
||||
return invalidXmlError
|
||||
}
|
||||
}
|
||||
|
||||
if t, ok := tok.(xml.StartElement); ok {
|
||||
typeName = t.Name.Local
|
||||
break
|
||||
}
|
||||
|
||||
// Treat value data without type identifier as string
|
||||
if t, ok := tok.(xml.CharData); ok {
|
||||
if value := strings.TrimSpace(string(t)); value != "" {
|
||||
if err = checkType(val, reflect.String); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
val.SetString(value)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch typeName {
|
||||
case "struct":
|
||||
ismap := false
|
||||
pmap := val
|
||||
valType := val.Type()
|
||||
|
||||
if err = checkType(val, reflect.Struct); err != nil {
|
||||
if checkType(val, reflect.Map) == nil {
|
||||
if valType.Key().Kind() != reflect.String {
|
||||
return fmt.Errorf("only maps with string key type can be unmarshalled")
|
||||
}
|
||||
ismap = true
|
||||
} else if checkType(val, reflect.Interface) == nil && val.IsNil() {
|
||||
var dummy map[string]interface{}
|
||||
pmap = reflect.New(reflect.TypeOf(dummy)).Elem()
|
||||
valType = pmap.Type()
|
||||
ismap = true
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var fields map[string]reflect.Value
|
||||
|
||||
if !ismap {
|
||||
fields = make(map[string]reflect.Value)
|
||||
|
||||
for i := 0; i < valType.NumField(); i++ {
|
||||
field := valType.Field(i)
|
||||
fieldVal := val.FieldByName(field.Name)
|
||||
|
||||
if fieldVal.CanSet() {
|
||||
if fn := field.Tag.Get("xmlrpc"); fn != "" {
|
||||
fields[fn] = fieldVal
|
||||
} else {
|
||||
fields[field.Name] = fieldVal
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Create initial empty map
|
||||
pmap.Set(reflect.MakeMap(valType))
|
||||
}
|
||||
|
||||
// Process struct members.
|
||||
StructLoop:
|
||||
for {
|
||||
if tok, err = dec.Token(); err != nil {
|
||||
return err
|
||||
}
|
||||
switch t := tok.(type) {
|
||||
case xml.StartElement:
|
||||
if t.Name.Local != "member" {
|
||||
return invalidXmlError
|
||||
}
|
||||
|
||||
tagName, fieldName, err := dec.readTag()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tagName != "name" {
|
||||
return invalidXmlError
|
||||
}
|
||||
|
||||
var fv reflect.Value
|
||||
ok := true
|
||||
|
||||
if !ismap {
|
||||
fv, ok = fields[string(fieldName)]
|
||||
} else {
|
||||
fv = reflect.New(valType.Elem())
|
||||
}
|
||||
|
||||
if ok {
|
||||
for {
|
||||
if tok, err = dec.Token(); err != nil {
|
||||
return err
|
||||
}
|
||||
if t, ok := tok.(xml.StartElement); ok && t.Name.Local == "value" {
|
||||
if err = dec.decodeValue(fv); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// </value>
|
||||
if err = dec.Skip(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// </member>
|
||||
if err = dec.Skip(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ismap {
|
||||
pmap.SetMapIndex(reflect.ValueOf(string(fieldName)), reflect.Indirect(fv))
|
||||
val.Set(pmap)
|
||||
}
|
||||
case xml.EndElement:
|
||||
break StructLoop
|
||||
}
|
||||
}
|
||||
case "array":
|
||||
pslice := val
|
||||
if checkType(val, reflect.Interface) == nil && val.IsNil() {
|
||||
var dummy []interface{}
|
||||
pslice = reflect.New(reflect.TypeOf(dummy)).Elem()
|
||||
} else if err = checkType(val, reflect.Slice); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ArrayLoop:
|
||||
for {
|
||||
if tok, err = dec.Token(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch t := tok.(type) {
|
||||
case xml.StartElement:
|
||||
if t.Name.Local != "data" {
|
||||
return invalidXmlError
|
||||
}
|
||||
|
||||
slice := reflect.MakeSlice(pslice.Type(), 0, 0)
|
||||
|
||||
DataLoop:
|
||||
for {
|
||||
if tok, err = dec.Token(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch tt := tok.(type) {
|
||||
case xml.StartElement:
|
||||
if tt.Name.Local != "value" {
|
||||
return invalidXmlError
|
||||
}
|
||||
|
||||
v := reflect.New(pslice.Type().Elem())
|
||||
if err = dec.decodeValue(v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
slice = reflect.Append(slice, v.Elem())
|
||||
|
||||
// </value>
|
||||
if err = dec.Skip(); err != nil {
|
||||
return err
|
||||
}
|
||||
case xml.EndElement:
|
||||
pslice.Set(slice)
|
||||
val.Set(pslice)
|
||||
break DataLoop
|
||||
}
|
||||
}
|
||||
case xml.EndElement:
|
||||
break ArrayLoop
|
||||
}
|
||||
}
|
||||
default:
|
||||
if tok, err = dec.Token(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var data []byte
|
||||
|
||||
switch t := tok.(type) {
|
||||
case xml.EndElement:
|
||||
return nil
|
||||
case xml.CharData:
|
||||
data = []byte(t.Copy())
|
||||
default:
|
||||
return invalidXmlError
|
||||
}
|
||||
|
||||
switch typeName {
|
||||
case "int", "i4", "i8":
|
||||
if checkType(val, reflect.Interface) == nil && val.IsNil() {
|
||||
i, err := strconv.ParseInt(string(data), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pi := reflect.New(reflect.TypeOf(i)).Elem()
|
||||
pi.SetInt(i)
|
||||
val.Set(pi)
|
||||
} else if err = checkType(val, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64); err != nil {
|
||||
return err
|
||||
} else {
|
||||
i, err := strconv.ParseInt(string(data), 10, val.Type().Bits())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
val.SetInt(i)
|
||||
}
|
||||
case "string", "base64":
|
||||
str := string(data)
|
||||
if checkType(val, reflect.Interface) == nil && val.IsNil() {
|
||||
pstr := reflect.New(reflect.TypeOf(str)).Elem()
|
||||
pstr.SetString(str)
|
||||
val.Set(pstr)
|
||||
} else if err = checkType(val, reflect.String); err != nil {
|
||||
return err
|
||||
} else {
|
||||
val.SetString(str)
|
||||
}
|
||||
case "dateTime.iso8601":
|
||||
var t time.Time
|
||||
var err error
|
||||
|
||||
for _, layout := range timeLayouts {
|
||||
t, err = time.Parse(layout, string(data))
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if checkType(val, reflect.Interface) == nil && val.IsNil() {
|
||||
ptime := reflect.New(reflect.TypeOf(t)).Elem()
|
||||
ptime.Set(reflect.ValueOf(t))
|
||||
val.Set(ptime)
|
||||
} else if _, ok := val.Interface().(time.Time); !ok {
|
||||
return TypeMismatchError(fmt.Sprintf("error: type mismatch error - can't decode %v to time", val.Kind()))
|
||||
} else {
|
||||
val.Set(reflect.ValueOf(t))
|
||||
}
|
||||
case "boolean":
|
||||
v, err := strconv.ParseBool(string(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if checkType(val, reflect.Interface) == nil && val.IsNil() {
|
||||
pv := reflect.New(reflect.TypeOf(v)).Elem()
|
||||
pv.SetBool(v)
|
||||
val.Set(pv)
|
||||
} else if err = checkType(val, reflect.Bool); err != nil {
|
||||
return err
|
||||
} else {
|
||||
val.SetBool(v)
|
||||
}
|
||||
case "double":
|
||||
if checkType(val, reflect.Interface) == nil && val.IsNil() {
|
||||
i, err := strconv.ParseFloat(string(data), 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pdouble := reflect.New(reflect.TypeOf(i)).Elem()
|
||||
pdouble.SetFloat(i)
|
||||
val.Set(pdouble)
|
||||
} else if err = checkType(val, reflect.Float32, reflect.Float64); err != nil {
|
||||
return err
|
||||
} else {
|
||||
i, err := strconv.ParseFloat(string(data), val.Type().Bits())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
val.SetFloat(i)
|
||||
}
|
||||
default:
|
||||
return errors.New("unsupported type")
|
||||
}
|
||||
|
||||
// </type>
|
||||
if err = dec.Skip(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dec *decoder) readTag() (string, []byte, error) {
|
||||
var tok xml.Token
|
||||
var err error
|
||||
|
||||
var name string
|
||||
for {
|
||||
if tok, err = dec.Token(); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if t, ok := tok.(xml.StartElement); ok {
|
||||
name = t.Name.Local
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
value, err := dec.readCharData()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
return name, value, dec.Skip()
|
||||
}
|
||||
|
||||
func (dec *decoder) readCharData() ([]byte, error) {
|
||||
var tok xml.Token
|
||||
var err error
|
||||
|
||||
if tok, err = dec.Token(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if t, ok := tok.(xml.CharData); ok {
|
||||
return []byte(t.Copy()), nil
|
||||
} else {
|
||||
return nil, invalidXmlError
|
||||
}
|
||||
}
|
||||
|
||||
func checkType(val reflect.Value, kinds ...reflect.Kind) error {
|
||||
if len(kinds) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if val.Kind() == reflect.Ptr {
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
match := false
|
||||
|
||||
for _, kind := range kinds {
|
||||
if val.Kind() == kind {
|
||||
match = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !match {
|
||||
return TypeMismatchError(fmt.Sprintf("error: type mismatch - can't unmarshal %v to %v",
|
||||
val.Kind(), kinds[0]))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
164
vendor/github.com/kolo/xmlrpc/encoder.go
generated
vendored
Normal file
164
vendor/github.com/kolo/xmlrpc/encoder.go
generated
vendored
Normal file
|
@ -0,0 +1,164 @@
|
|||
package xmlrpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type encodeFunc func(reflect.Value) ([]byte, error)
|
||||
|
||||
func marshal(v interface{}) ([]byte, error) {
|
||||
if v == nil {
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
val := reflect.ValueOf(v)
|
||||
return encodeValue(val)
|
||||
}
|
||||
|
||||
func encodeValue(val reflect.Value) ([]byte, error) {
|
||||
var b []byte
|
||||
var err error
|
||||
|
||||
if val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface {
|
||||
if val.IsNil() {
|
||||
return []byte("<value/>"), nil
|
||||
}
|
||||
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
switch val.Kind() {
|
||||
case reflect.Struct:
|
||||
switch val.Interface().(type) {
|
||||
case time.Time:
|
||||
t := val.Interface().(time.Time)
|
||||
b = []byte(fmt.Sprintf("<dateTime.iso8601>%s</dateTime.iso8601>", t.Format(iso8601)))
|
||||
default:
|
||||
b, err = encodeStruct(val)
|
||||
}
|
||||
case reflect.Map:
|
||||
b, err = encodeMap(val)
|
||||
case reflect.Slice:
|
||||
b, err = encodeSlice(val)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
b = []byte(fmt.Sprintf("<int>%s</int>", strconv.FormatInt(val.Int(), 10)))
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
b = []byte(fmt.Sprintf("<i4>%s</i4>", strconv.FormatUint(val.Uint(), 10)))
|
||||
case reflect.Float32, reflect.Float64:
|
||||
b = []byte(fmt.Sprintf("<double>%s</double>",
|
||||
strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits())))
|
||||
case reflect.Bool:
|
||||
if val.Bool() {
|
||||
b = []byte("<boolean>1</boolean>")
|
||||
} else {
|
||||
b = []byte("<boolean>0</boolean>")
|
||||
}
|
||||
case reflect.String:
|
||||
var buf bytes.Buffer
|
||||
|
||||
xml.Escape(&buf, []byte(val.String()))
|
||||
|
||||
if _, ok := val.Interface().(Base64); ok {
|
||||
b = []byte(fmt.Sprintf("<base64>%s</base64>", buf.String()))
|
||||
} else {
|
||||
b = []byte(fmt.Sprintf("<string>%s</string>", buf.String()))
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("xmlrpc encode error: unsupported type")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []byte(fmt.Sprintf("<value>%s</value>", string(b))), nil
|
||||
}
|
||||
|
||||
func encodeStruct(val reflect.Value) ([]byte, error) {
|
||||
var b bytes.Buffer
|
||||
|
||||
b.WriteString("<struct>")
|
||||
|
||||
t := val.Type()
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
b.WriteString("<member>")
|
||||
f := t.Field(i)
|
||||
|
||||
name := f.Tag.Get("xmlrpc")
|
||||
if name == "" {
|
||||
name = f.Name
|
||||
}
|
||||
b.WriteString(fmt.Sprintf("<name>%s</name>", name))
|
||||
|
||||
p, err := encodeValue(val.FieldByName(f.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.Write(p)
|
||||
|
||||
b.WriteString("</member>")
|
||||
}
|
||||
|
||||
b.WriteString("</struct>")
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
func encodeMap(val reflect.Value) ([]byte, error) {
|
||||
var t = val.Type()
|
||||
|
||||
if t.Key().Kind() != reflect.String {
|
||||
return nil, fmt.Errorf("xmlrpc encode error: only maps with string keys are supported")
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
|
||||
b.WriteString("<struct>")
|
||||
|
||||
keys := val.MapKeys()
|
||||
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
key := keys[i]
|
||||
kval := val.MapIndex(key)
|
||||
|
||||
b.WriteString("<member>")
|
||||
b.WriteString(fmt.Sprintf("<name>%s</name>", key.String()))
|
||||
|
||||
p, err := encodeValue(kval)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b.Write(p)
|
||||
b.WriteString("</member>")
|
||||
}
|
||||
|
||||
b.WriteString("</struct>")
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
func encodeSlice(val reflect.Value) ([]byte, error) {
|
||||
var b bytes.Buffer
|
||||
|
||||
b.WriteString("<array><data>")
|
||||
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
p, err := encodeValue(val.Index(i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b.Write(p)
|
||||
}
|
||||
|
||||
b.WriteString("</data></array>")
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
57
vendor/github.com/kolo/xmlrpc/request.go
generated
vendored
Normal file
57
vendor/github.com/kolo/xmlrpc/request.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
package xmlrpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func NewRequest(url string, method string, args interface{}) (*http.Request, error) {
|
||||
var t []interface{}
|
||||
var ok bool
|
||||
if t, ok = args.([]interface{}); !ok {
|
||||
if args != nil {
|
||||
t = []interface{}{args}
|
||||
}
|
||||
}
|
||||
|
||||
body, err := EncodeMethodCall(method, t...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
request, err := http.NewRequest("POST", url, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
request.Header.Set("Content-Type", "text/xml")
|
||||
request.Header.Set("Content-Length", fmt.Sprintf("%d", len(body)))
|
||||
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func EncodeMethodCall(method string, args ...interface{}) ([]byte, error) {
|
||||
var b bytes.Buffer
|
||||
b.WriteString(`<?xml version="1.0" encoding="UTF-8"?>`)
|
||||
b.WriteString(fmt.Sprintf("<methodCall><methodName>%s</methodName>", method))
|
||||
|
||||
if args != nil {
|
||||
b.WriteString("<params>")
|
||||
|
||||
for _, arg := range args {
|
||||
p, err := marshal(arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b.WriteString(fmt.Sprintf("<param>%s</param>", string(p)))
|
||||
}
|
||||
|
||||
b.WriteString("</params>")
|
||||
}
|
||||
|
||||
b.WriteString("</methodCall>")
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
52
vendor/github.com/kolo/xmlrpc/response.go
generated
vendored
Normal file
52
vendor/github.com/kolo/xmlrpc/response.go
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
package xmlrpc
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var (
|
||||
faultRx = regexp.MustCompile(`<fault>(\s|\S)+</fault>`)
|
||||
)
|
||||
|
||||
type failedResponse struct {
|
||||
Code int `xmlrpc:"faultCode"`
|
||||
Error string `xmlrpc:"faultString"`
|
||||
}
|
||||
|
||||
func (r *failedResponse) err() error {
|
||||
return &xmlrpcError{
|
||||
code: r.Code,
|
||||
err: r.Error,
|
||||
}
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
func NewResponse(data []byte) *Response {
|
||||
return &Response{
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Response) Failed() bool {
|
||||
return faultRx.Match(r.data)
|
||||
}
|
||||
|
||||
func (r *Response) Err() error {
|
||||
failedResp := new(failedResponse)
|
||||
if err := unmarshal(r.data, failedResp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return failedResp.err()
|
||||
}
|
||||
|
||||
func (r *Response) Unmarshal(v interface{}) error {
|
||||
if err := unmarshal(r.data, v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
19
vendor/github.com/kolo/xmlrpc/xmlrpc.go
generated
vendored
Normal file
19
vendor/github.com/kolo/xmlrpc/xmlrpc.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
package xmlrpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// xmlrpcError represents errors returned on xmlrpc request.
|
||||
type xmlrpcError struct {
|
||||
code int
|
||||
err string
|
||||
}
|
||||
|
||||
// Error() method implements Error interface
|
||||
func (e *xmlrpcError) Error() string {
|
||||
return fmt.Sprintf("error: \"%s\" code: %d", e.err, e.code)
|
||||
}
|
||||
|
||||
// Base64 represents value in base64 encoding
|
||||
type Base64 string
|
21
vendor/github.com/mitchellh/mapstructure/LICENSE
generated
vendored
Normal file
21
vendor/github.com/mitchellh/mapstructure/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Mitchell Hashimoto
|
||||
|
||||
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.
|
217
vendor/github.com/mitchellh/mapstructure/decode_hooks.go
generated
vendored
Normal file
217
vendor/github.com/mitchellh/mapstructure/decode_hooks.go
generated
vendored
Normal file
|
@ -0,0 +1,217 @@
|
|||
package mapstructure
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// typedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns
|
||||
// it into the proper DecodeHookFunc type, such as DecodeHookFuncType.
|
||||
func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
|
||||
// Create variables here so we can reference them with the reflect pkg
|
||||
var f1 DecodeHookFuncType
|
||||
var f2 DecodeHookFuncKind
|
||||
|
||||
// Fill in the variables into this interface and the rest is done
|
||||
// automatically using the reflect package.
|
||||
potential := []interface{}{f1, f2}
|
||||
|
||||
v := reflect.ValueOf(h)
|
||||
vt := v.Type()
|
||||
for _, raw := range potential {
|
||||
pt := reflect.ValueOf(raw).Type()
|
||||
if vt.ConvertibleTo(pt) {
|
||||
return v.Convert(pt).Interface()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecodeHookExec executes the given decode hook. This should be used
|
||||
// since it'll naturally degrade to the older backwards compatible DecodeHookFunc
|
||||
// that took reflect.Kind instead of reflect.Type.
|
||||
func DecodeHookExec(
|
||||
raw DecodeHookFunc,
|
||||
from reflect.Type, to reflect.Type,
|
||||
data interface{}) (interface{}, error) {
|
||||
switch f := typedDecodeHook(raw).(type) {
|
||||
case DecodeHookFuncType:
|
||||
return f(from, to, data)
|
||||
case DecodeHookFuncKind:
|
||||
return f(from.Kind(), to.Kind(), data)
|
||||
default:
|
||||
return nil, errors.New("invalid decode hook signature")
|
||||
}
|
||||
}
|
||||
|
||||
// ComposeDecodeHookFunc creates a single DecodeHookFunc that
|
||||
// automatically composes multiple DecodeHookFuncs.
|
||||
//
|
||||
// The composed funcs are called in order, with the result of the
|
||||
// previous transformation.
|
||||
func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
|
||||
return func(
|
||||
f reflect.Type,
|
||||
t reflect.Type,
|
||||
data interface{}) (interface{}, error) {
|
||||
var err error
|
||||
for _, f1 := range fs {
|
||||
data, err = DecodeHookExec(f1, f, t, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Modify the from kind to be correct with the new data
|
||||
f = nil
|
||||
if val := reflect.ValueOf(data); val.IsValid() {
|
||||
f = val.Type()
|
||||
}
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
// StringToSliceHookFunc returns a DecodeHookFunc that converts
|
||||
// string to []string by splitting on the given sep.
|
||||
func StringToSliceHookFunc(sep string) DecodeHookFunc {
|
||||
return func(
|
||||
f reflect.Kind,
|
||||
t reflect.Kind,
|
||||
data interface{}) (interface{}, error) {
|
||||
if f != reflect.String || t != reflect.Slice {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
raw := data.(string)
|
||||
if raw == "" {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
return strings.Split(raw, sep), nil
|
||||
}
|
||||
}
|
||||
|
||||
// StringToTimeDurationHookFunc returns a DecodeHookFunc that converts
|
||||
// strings to time.Duration.
|
||||
func StringToTimeDurationHookFunc() DecodeHookFunc {
|
||||
return func(
|
||||
f reflect.Type,
|
||||
t reflect.Type,
|
||||
data interface{}) (interface{}, error) {
|
||||
if f.Kind() != reflect.String {
|
||||
return data, nil
|
||||
}
|
||||
if t != reflect.TypeOf(time.Duration(5)) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Convert it by parsing
|
||||
return time.ParseDuration(data.(string))
|
||||
}
|
||||
}
|
||||
|
||||
// StringToIPHookFunc returns a DecodeHookFunc that converts
|
||||
// strings to net.IP
|
||||
func StringToIPHookFunc() DecodeHookFunc {
|
||||
return func(
|
||||
f reflect.Type,
|
||||
t reflect.Type,
|
||||
data interface{}) (interface{}, error) {
|
||||
if f.Kind() != reflect.String {
|
||||
return data, nil
|
||||
}
|
||||
if t != reflect.TypeOf(net.IP{}) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Convert it by parsing
|
||||
ip := net.ParseIP(data.(string))
|
||||
if ip == nil {
|
||||
return net.IP{}, fmt.Errorf("failed parsing ip %v", data)
|
||||
}
|
||||
|
||||
return ip, nil
|
||||
}
|
||||
}
|
||||
|
||||
// StringToIPNetHookFunc returns a DecodeHookFunc that converts
|
||||
// strings to net.IPNet
|
||||
func StringToIPNetHookFunc() DecodeHookFunc {
|
||||
return func(
|
||||
f reflect.Type,
|
||||
t reflect.Type,
|
||||
data interface{}) (interface{}, error) {
|
||||
if f.Kind() != reflect.String {
|
||||
return data, nil
|
||||
}
|
||||
if t != reflect.TypeOf(net.IPNet{}) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Convert it by parsing
|
||||
_, net, err := net.ParseCIDR(data.(string))
|
||||
return net, err
|
||||
}
|
||||
}
|
||||
|
||||
// StringToTimeHookFunc returns a DecodeHookFunc that converts
|
||||
// strings to time.Time.
|
||||
func StringToTimeHookFunc(layout string) DecodeHookFunc {
|
||||
return func(
|
||||
f reflect.Type,
|
||||
t reflect.Type,
|
||||
data interface{}) (interface{}, error) {
|
||||
if f.Kind() != reflect.String {
|
||||
return data, nil
|
||||
}
|
||||
if t != reflect.TypeOf(time.Time{}) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Convert it by parsing
|
||||
return time.Parse(layout, data.(string))
|
||||
}
|
||||
}
|
||||
|
||||
// WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to
|
||||
// the decoder.
|
||||
//
|
||||
// Note that this is significantly different from the WeaklyTypedInput option
|
||||
// of the DecoderConfig.
|
||||
func WeaklyTypedHook(
|
||||
f reflect.Kind,
|
||||
t reflect.Kind,
|
||||
data interface{}) (interface{}, error) {
|
||||
dataVal := reflect.ValueOf(data)
|
||||
switch t {
|
||||
case reflect.String:
|
||||
switch f {
|
||||
case reflect.Bool:
|
||||
if dataVal.Bool() {
|
||||
return "1", nil
|
||||
}
|
||||
return "0", nil
|
||||
case reflect.Float32:
|
||||
return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil
|
||||
case reflect.Int:
|
||||
return strconv.FormatInt(dataVal.Int(), 10), nil
|
||||
case reflect.Slice:
|
||||
dataType := dataVal.Type()
|
||||
elemKind := dataType.Elem().Kind()
|
||||
if elemKind == reflect.Uint8 {
|
||||
return string(dataVal.Interface().([]uint8)), nil
|
||||
}
|
||||
case reflect.Uint:
|
||||
return strconv.FormatUint(dataVal.Uint(), 10), nil
|
||||
}
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
50
vendor/github.com/mitchellh/mapstructure/error.go
generated
vendored
Normal file
50
vendor/github.com/mitchellh/mapstructure/error.go
generated
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
package mapstructure
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Error implements the error interface and can represents multiple
|
||||
// errors that occur in the course of a single decode.
|
||||
type Error struct {
|
||||
Errors []string
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
points := make([]string, len(e.Errors))
|
||||
for i, err := range e.Errors {
|
||||
points[i] = fmt.Sprintf("* %s", err)
|
||||
}
|
||||
|
||||
sort.Strings(points)
|
||||
return fmt.Sprintf(
|
||||
"%d error(s) decoding:\n\n%s",
|
||||
len(e.Errors), strings.Join(points, "\n"))
|
||||
}
|
||||
|
||||
// WrappedErrors implements the errwrap.Wrapper interface to make this
|
||||
// return value more useful with the errwrap and go-multierror libraries.
|
||||
func (e *Error) WrappedErrors() []error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := make([]error, len(e.Errors))
|
||||
for i, e := range e.Errors {
|
||||
result[i] = errors.New(e)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func appendErrors(errors []string, err error) []string {
|
||||
switch e := err.(type) {
|
||||
case *Error:
|
||||
return append(errors, e.Errors...)
|
||||
default:
|
||||
return append(errors, e.Error())
|
||||
}
|
||||
}
|
1149
vendor/github.com/mitchellh/mapstructure/mapstructure.go
generated
vendored
Normal file
1149
vendor/github.com/mitchellh/mapstructure/mapstructure.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
21
vendor/github.com/smueller18/goinwx/LICENSE
generated
vendored
Normal file
21
vendor/github.com/smueller18/goinwx/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 Andrew
|
||||
|
||||
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
vendor/github.com/smueller18/goinwx/account.go
generated
vendored
Normal file
54
vendor/github.com/smueller18/goinwx/account.go
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
package goinwx
|
||||
|
||||
const (
|
||||
methodAccountLogin = "account.login"
|
||||
methodAccountLogout = "account.logout"
|
||||
methodAccountLock = "account.lock"
|
||||
methodAccountUnlock = "account.unlock"
|
||||
)
|
||||
|
||||
type AccountService interface {
|
||||
Login() error
|
||||
Logout() error
|
||||
Lock() error
|
||||
Unlock(tan string) error
|
||||
}
|
||||
|
||||
type AccountServiceOp struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
var _ AccountService = &AccountServiceOp{}
|
||||
|
||||
func (s *AccountServiceOp) Login() error {
|
||||
req := s.client.NewRequest(methodAccountLogin, map[string]interface{}{
|
||||
"user": s.client.Username,
|
||||
"pass": s.client.Password,
|
||||
})
|
||||
|
||||
_, err := s.client.Do(*req)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *AccountServiceOp) Logout() error {
|
||||
req := s.client.NewRequest(methodAccountLogout, nil)
|
||||
|
||||
_, err := s.client.Do(*req)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *AccountServiceOp) Lock() error {
|
||||
req := s.client.NewRequest(methodAccountLock, nil)
|
||||
|
||||
_, err := s.client.Do(*req)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *AccountServiceOp) Unlock(tan string) error {
|
||||
req := s.client.NewRequest(methodAccountUnlock, map[string]interface{}{
|
||||
"tan": tan,
|
||||
})
|
||||
|
||||
_, err := s.client.Do(*req)
|
||||
return err
|
||||
}
|
150
vendor/github.com/smueller18/goinwx/contact.go
generated
vendored
Normal file
150
vendor/github.com/smueller18/goinwx/contact.go
generated
vendored
Normal file
|
@ -0,0 +1,150 @@
|
|||
package goinwx
|
||||
|
||||
import (
|
||||
"github.com/fatih/structs"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
const (
|
||||
methodContactInfo = "contact.info"
|
||||
methodContactList = "contact.list"
|
||||
methodContactCreate = "contact.create"
|
||||
methodContactDelete = "contact.delete"
|
||||
methodContactUpdate = "contact.update"
|
||||
)
|
||||
|
||||
type ContactService interface {
|
||||
Create(*ContactCreateRequest) (int, error)
|
||||
Update(*ContactUpdateRequest) error
|
||||
Delete(int) error
|
||||
Info(int) (*ContactInfoResponse, error)
|
||||
List(string) (*ContactListResponse, error)
|
||||
}
|
||||
|
||||
type ContactServiceOp struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
var _ ContactService = &ContactServiceOp{}
|
||||
|
||||
type ContactCreateRequest struct {
|
||||
Type string `structs:"type"`
|
||||
Name string `structs:"name"`
|
||||
Org string `structs:"org,omitempty"`
|
||||
Street string `structs:"street"`
|
||||
City string `structs:"city"`
|
||||
PostalCode string `structs:"pc"`
|
||||
StateProvince string `structs:"sp,omitempty"`
|
||||
CountryCode string `structs:"cc"`
|
||||
Voice string `structs:"voice"`
|
||||
Fax string `structs:"fax,omitempty"`
|
||||
Email string `structs:"email"`
|
||||
Remarks string `structs:"remarks,omitempty"`
|
||||
Protection bool `structs:"protection,omitempty"`
|
||||
Testing bool `structs:"testing,omitempty"`
|
||||
}
|
||||
|
||||
type ContactUpdateRequest struct {
|
||||
Id int `structs:"id"`
|
||||
Name string `structs:"name,omitempty"`
|
||||
Org string `structs:"org,omitempty"`
|
||||
Street string `structs:"street,omitempty"`
|
||||
City string `structs:"city,omitempty"`
|
||||
PostalCode string `structs:"pc,omitempty"`
|
||||
StateProvince string `structs:"sp,omitempty"`
|
||||
CountryCode string `structs:"cc,omitempty"`
|
||||
Voice string `structs:"voice,omitempty"`
|
||||
Fax string `structs:"fax,omitempty"`
|
||||
Email string `structs:"email,omitempty"`
|
||||
Remarks string `structs:"remarks,omitempty"`
|
||||
Protection bool `structs:"protection,omitempty"`
|
||||
Testing bool `structs:"testing,omitempty"`
|
||||
}
|
||||
|
||||
type ContactInfoResponse struct {
|
||||
Contact Contact `mapstructure:"contact"`
|
||||
}
|
||||
|
||||
type ContactListResponse struct {
|
||||
Count int
|
||||
Contacts []Contact `mapstructure:"contact"`
|
||||
}
|
||||
|
||||
func (s *ContactServiceOp) Create(request *ContactCreateRequest) (int, error) {
|
||||
req := s.client.NewRequest(methodContactCreate, structs.Map(request))
|
||||
|
||||
resp, err := s.client.Do(*req)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var result map[string]int
|
||||
err = mapstructure.Decode(*resp, &result)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return result["id"], nil
|
||||
}
|
||||
|
||||
func (s *ContactServiceOp) Delete(roId int) error {
|
||||
req := s.client.NewRequest(methodContactDelete, map[string]interface{}{
|
||||
"id": roId,
|
||||
})
|
||||
|
||||
_, err := s.client.Do(*req)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *ContactServiceOp) Update(request *ContactUpdateRequest) error {
|
||||
req := s.client.NewRequest(methodContactUpdate, structs.Map(request))
|
||||
|
||||
_, err := s.client.Do(*req)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *ContactServiceOp) Info(contactId int) (*ContactInfoResponse, error) {
|
||||
var requestMap = make(map[string]interface{})
|
||||
requestMap["wide"] = 1
|
||||
|
||||
if contactId != 0 {
|
||||
requestMap["id"] = contactId
|
||||
}
|
||||
|
||||
req := s.client.NewRequest(methodContactInfo, requestMap)
|
||||
|
||||
resp, err := s.client.Do(*req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result ContactInfoResponse
|
||||
err = mapstructure.Decode(*resp, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (s *ContactServiceOp) List(search string) (*ContactListResponse, error) {
|
||||
var requestMap = make(map[string]interface{})
|
||||
|
||||
if search != "" {
|
||||
requestMap["search"] = search
|
||||
}
|
||||
req := s.client.NewRequest(methodContactList, requestMap)
|
||||
|
||||
resp, err := s.client.Do(*req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result ContactListResponse
|
||||
err = mapstructure.Decode(*resp, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
303
vendor/github.com/smueller18/goinwx/domain.go
generated
vendored
Normal file
303
vendor/github.com/smueller18/goinwx/domain.go
generated
vendored
Normal file
|
@ -0,0 +1,303 @@
|
|||
package goinwx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/structs"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
const (
|
||||
methodDomainCheck = "domain.check"
|
||||
methodDomainCreate = "domain.create"
|
||||
methodDomainDelete = "domain.delete"
|
||||
methodDomainGetPrices = "domain.getPrices"
|
||||
methodDomainGetRules = "domain.getRules"
|
||||
methodDomainInfo = "domain.info"
|
||||
methodDomainList = "domain.list"
|
||||
methodDomainLog = "domain.log"
|
||||
methodDomainPush = "domain.push"
|
||||
methodDomainRenew = "domain.renew"
|
||||
methodDomainRestore = "domain.restore"
|
||||
methodDomainStats = "domain.stats"
|
||||
methodDomainTrade = "domain.trade"
|
||||
methodDomainTransfer = "domain.transfer"
|
||||
methodDomainTransferOut = "domain.transferOut"
|
||||
methodDomainUpdate = "domain.update"
|
||||
methodDomainWhois = "domain.whois"
|
||||
)
|
||||
|
||||
type DomainService interface {
|
||||
Check(domains []string) ([]DomainCheckResponse, error)
|
||||
Register(request *DomainRegisterRequest) (*DomainRegisterResponse, error)
|
||||
Delete(domain string, scheduledDate time.Time) error
|
||||
Info(domain string, roId int) (*DomainInfoResponse, error)
|
||||
GetPrices(tlds []string) ([]DomainPriceResponse, error)
|
||||
List(*DomainListRequest) (*DomainList, error)
|
||||
Whois(domain string) (string, error)
|
||||
}
|
||||
|
||||
type DomainServiceOp struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
var _ DomainService = &DomainServiceOp{}
|
||||
|
||||
type domainCheckResponseRoot struct {
|
||||
Domains []DomainCheckResponse `mapstructure:"domain"`
|
||||
}
|
||||
type DomainCheckResponse struct {
|
||||
Available int `mapstructure:"avail"`
|
||||
Status string `mapstructure:"status"`
|
||||
Name string `mapstructure:"name"`
|
||||
Domain string `mapstructure:"domain"`
|
||||
TLD string `mapstructure:"tld"`
|
||||
CheckMethod string `mapstructure:"checkmethod"`
|
||||
Price float32 `mapstructure:"price"`
|
||||
CheckTime float32 `mapstructure:"checktime"`
|
||||
}
|
||||
|
||||
type domainPriceResponseRoot struct {
|
||||
Prices []DomainPriceResponse `mapstructure:"price"`
|
||||
}
|
||||
type DomainPriceResponse struct {
|
||||
Tld string `mapstructure:"tld"`
|
||||
Currency string `mapstructure:"currency"`
|
||||
CreatePrice float32 `mapstructure:"createPrice"`
|
||||
MonthlyCreatePrice float32 `mapstructure:"monthlyCreatePrice"`
|
||||
TransferPrice float32 `mapstructure:"transferPrice"`
|
||||
RenewalPrice float32 `mapstructure:"renewalPrice"`
|
||||
MonthlyRenewalPrice float32 `mapstructure:"monthlyRenewalPrice"`
|
||||
UpdatePrice float32 `mapstructure:"updatePrice"`
|
||||
TradePrice float32 `mapstructure:"tradePrice"`
|
||||
TrusteePrice float32 `mapstructure:"trusteePrice"`
|
||||
MonthlyTrusteePrice float32 `mapstructure:"monthlyTrusteePrice"`
|
||||
CreatePeriod int `mapstructure:"createPeriod"`
|
||||
TransferPeriod int `mapstructure:"transferPeriod"`
|
||||
RenewalPeriod int `mapstructure:"renewalPeriod"`
|
||||
TradePeriod int `mapstructure:"tradePeriod"`
|
||||
}
|
||||
|
||||
type DomainRegisterRequest struct {
|
||||
Domain string `structs:"domain"`
|
||||
Period string `structs:"period,omitempty"`
|
||||
Registrant int `structs:"registrant"`
|
||||
Admin int `structs:"admin"`
|
||||
Tech int `structs:"tech"`
|
||||
Billing int `structs:"billing"`
|
||||
Nameservers []string `structs:"ns,omitempty"`
|
||||
TransferLock string `structs:"transferLock,omitempty"`
|
||||
RenewalMode string `structs:"renewalMode,omitempty"`
|
||||
WhoisProvider string `structs:"whoisProvider,omitempty"`
|
||||
WhoisUrl string `structs:"whoisUrl,omitempty"`
|
||||
ScDate string `structs:"scDate,omitempty"`
|
||||
ExtDate string `structs:"extDate,omitempty"`
|
||||
Asynchron string `structs:"asynchron,omitempty"`
|
||||
Voucher string `structs:"voucher,omitempty"`
|
||||
Testing string `structs:"testing,omitempty"`
|
||||
}
|
||||
|
||||
type DomainRegisterResponse struct {
|
||||
RoId int
|
||||
Price float32
|
||||
Currency string
|
||||
}
|
||||
|
||||
type DomainInfoResponse struct {
|
||||
RoId int `mapstructure:"roId"`
|
||||
Domain string `mapstructure:"domain"`
|
||||
DomainAce string `mapstructure:"domainAce"`
|
||||
Period string `mapstructure:"period"`
|
||||
CrDate time.Time `mapstructure:"crDate"`
|
||||
ExDate time.Time `mapstructure:"exDate"`
|
||||
UpDate time.Time `mapstructure:"upDate"`
|
||||
ReDate time.Time `mapstructure:"reDate"`
|
||||
ScDate time.Time `mapstructure:"scDate"`
|
||||
TransferLock int `mapstructure:"transferLock"`
|
||||
Status string `mapstructure:"status"`
|
||||
AuthCode string `mapstructure:"authCode"`
|
||||
RenewalMode string `mapstructure:"renewalMode"`
|
||||
TransferMode string `mapstructure:"transferMode"`
|
||||
Registrant int `mapstructure:"registrant"`
|
||||
Admin int `mapstructure:"admin"`
|
||||
Tech int `mapstructure:"tech"`
|
||||
Billing int `mapstructure:"billing"`
|
||||
Nameservers []string `mapstructure:"ns"`
|
||||
NoDelegation string `mapstructure:"noDelegation"`
|
||||
Contacts map[string]Contact `mapstructure:"contact"`
|
||||
}
|
||||
|
||||
type Contact struct {
|
||||
RoId int
|
||||
Id string
|
||||
Type string
|
||||
Name string
|
||||
Org string
|
||||
Street string
|
||||
City string
|
||||
PostalCode string `mapstructure:"pc"`
|
||||
StateProvince string `mapstructure:"sp"`
|
||||
Country string `mapstructure:"cc"`
|
||||
Phone string `mapstructure:"voice"`
|
||||
Fax string
|
||||
Email string
|
||||
Remarks string
|
||||
Protection string
|
||||
}
|
||||
|
||||
type DomainListRequest struct {
|
||||
Domain string `structs:"domain,omitempty"`
|
||||
RoId int `structs:"roId,omitempty"`
|
||||
Status int `structs:"status,omitempty"`
|
||||
Registrant int `structs:"registrant,omitempty"`
|
||||
Admin int `structs:"admin,omitempty"`
|
||||
Tech int `structs:"tech,omitempty"`
|
||||
Billing int `structs:"billing,omitempty"`
|
||||
RenewalMode int `structs:"renewalMode,omitempty"`
|
||||
TransferLock int `structs:"transferLock,omitempty"`
|
||||
NoDelegation int `structs:"noDelegation,omitempty"`
|
||||
Tag int `structs:"tag,omitempty"`
|
||||
Order int `structs:"order,omitempty"`
|
||||
Page int `structs:"page,omitempty"`
|
||||
Pagelimit int `structs:"pagelimit,omitempty"`
|
||||
}
|
||||
|
||||
type DomainList struct {
|
||||
Count int
|
||||
Domains []DomainInfoResponse `mapstructure:"domain"`
|
||||
}
|
||||
|
||||
func (s *DomainServiceOp) Check(domains []string) ([]DomainCheckResponse, error) {
|
||||
req := s.client.NewRequest(methodDomainCheck, map[string]interface{}{
|
||||
"domain": domains,
|
||||
"wide": "2",
|
||||
})
|
||||
|
||||
resp, err := s.client.Do(*req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
root := new(domainCheckResponseRoot)
|
||||
err = mapstructure.Decode(*resp, &root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return root.Domains, nil
|
||||
}
|
||||
|
||||
func (s *DomainServiceOp) GetPrices(tlds []string) ([]DomainPriceResponse, error) {
|
||||
req := s.client.NewRequest(methodDomainGetPrices, map[string]interface{}{
|
||||
"tld": tlds,
|
||||
"vat": false,
|
||||
})
|
||||
|
||||
resp, err := s.client.Do(*req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
root := new(domainPriceResponseRoot)
|
||||
err = mapstructure.Decode(*resp, &root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return root.Prices, nil
|
||||
}
|
||||
|
||||
func (s *DomainServiceOp) Register(request *DomainRegisterRequest) (*DomainRegisterResponse, error) {
|
||||
req := s.client.NewRequest(methodDomainCreate, structs.Map(request))
|
||||
|
||||
resp, err := s.client.Do(*req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result DomainRegisterResponse
|
||||
err = mapstructure.Decode(*resp, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (s *DomainServiceOp) Delete(domain string, scheduledDate time.Time) error {
|
||||
req := s.client.NewRequest(methodDomainDelete, map[string]interface{}{
|
||||
"domain": domain,
|
||||
"scDate": scheduledDate.Format(time.RFC3339),
|
||||
})
|
||||
|
||||
_, err := s.client.Do(*req)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *DomainServiceOp) Info(domain string, roId int) (*DomainInfoResponse, error) {
|
||||
req := s.client.NewRequest(methodDomainInfo, map[string]interface{}{
|
||||
"domain": domain,
|
||||
"wide": "2",
|
||||
})
|
||||
if roId != 0 {
|
||||
req.Args["roId"] = roId
|
||||
}
|
||||
|
||||
resp, err := s.client.Do(*req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result DomainInfoResponse
|
||||
err = mapstructure.Decode(*resp, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Println("Response", result)
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (s *DomainServiceOp) List(request *DomainListRequest) (*DomainList, error) {
|
||||
if request == nil {
|
||||
return nil, errors.New("Request can't be nil")
|
||||
}
|
||||
requestMap := structs.Map(request)
|
||||
requestMap["wide"] = "2"
|
||||
|
||||
req := s.client.NewRequest(methodDomainList, requestMap)
|
||||
|
||||
resp, err := s.client.Do(*req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result DomainList
|
||||
err = mapstructure.Decode(*resp, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (s *DomainServiceOp) Whois(domain string) (string, error) {
|
||||
req := s.client.NewRequest(methodDomainWhois, map[string]interface{}{
|
||||
"domain": domain,
|
||||
})
|
||||
|
||||
resp, err := s.client.Do(*req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var result map[string]string
|
||||
err = mapstructure.Decode(*resp, &result)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return result["whois"], nil
|
||||
}
|
133
vendor/github.com/smueller18/goinwx/goinwx.go
generated
vendored
Normal file
133
vendor/github.com/smueller18/goinwx/goinwx.go
generated
vendored
Normal file
|
@ -0,0 +1,133 @@
|
|||
package goinwx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/kolo/xmlrpc"
|
||||
)
|
||||
|
||||
const (
|
||||
libraryVersion = "0.4.0"
|
||||
APIBaseUrl = "https://api.domrobot.com/xmlrpc/"
|
||||
APISandboxBaseUrl = "https://api.ote.domrobot.com/xmlrpc/"
|
||||
APILanguage = "eng"
|
||||
)
|
||||
|
||||
// Client manages communication with INWX API.
|
||||
type Client struct {
|
||||
// HTTP client used to communicate with the INWX API.
|
||||
RPCClient *xmlrpc.Client
|
||||
|
||||
// Base URL for API requests.
|
||||
BaseURL *url.URL
|
||||
|
||||
// API username
|
||||
Username string
|
||||
|
||||
// API password
|
||||
Password string
|
||||
|
||||
// User agent for client
|
||||
APILanguage string
|
||||
|
||||
// Services used for communicating with the API
|
||||
Account AccountService
|
||||
Domains DomainService
|
||||
Nameservers NameserverService
|
||||
Contacts ContactService
|
||||
}
|
||||
|
||||
type ClientOptions struct {
|
||||
Sandbox bool
|
||||
}
|
||||
|
||||
type Request struct {
|
||||
ServiceMethod string
|
||||
Args map[string]interface{}
|
||||
}
|
||||
|
||||
// Response is a INWX API response. This wraps the standard http.Response returned from INWX.
|
||||
type Response struct {
|
||||
Code int `xmlrpc:"code"`
|
||||
Message string `xmlrpc:"msg"`
|
||||
ReasonCode string `xmlrpc:"reasonCode"`
|
||||
Reason string `xmlrpc:"reason"`
|
||||
ResponseData map[string]interface{} `xmlrpc:"resData"`
|
||||
}
|
||||
|
||||
// An ErrorResponse reports the error caused by an API request
|
||||
type ErrorResponse struct {
|
||||
Code int `xmlrpc:"code"`
|
||||
Message string `xmlrpc:"msg"`
|
||||
ReasonCode string `xmlrpc:"reasonCode"`
|
||||
Reason string `xmlrpc:"reason"`
|
||||
}
|
||||
|
||||
// NewClient returns a new INWX API client.
|
||||
func NewClient(username, password string, opts *ClientOptions) *Client {
|
||||
var useSandbox bool
|
||||
if opts != nil {
|
||||
useSandbox = opts.Sandbox
|
||||
}
|
||||
|
||||
var baseURL *url.URL
|
||||
|
||||
if useSandbox {
|
||||
baseURL, _ = url.Parse(APISandboxBaseUrl)
|
||||
} else {
|
||||
baseURL, _ = url.Parse(APIBaseUrl)
|
||||
}
|
||||
|
||||
rpcClient, _ := xmlrpc.NewClient(baseURL.String(), nil)
|
||||
|
||||
client := &Client{RPCClient: rpcClient,
|
||||
BaseURL: baseURL,
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
|
||||
client.Account = &AccountServiceOp{client: client}
|
||||
client.Domains = &DomainServiceOp{client: client}
|
||||
client.Nameservers = &NameserverServiceOp{client: client}
|
||||
client.Contacts = &ContactServiceOp{client: client}
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
// NewRequest creates an API request.
|
||||
func (c *Client) NewRequest(serviceMethod string, args map[string]interface{}) *Request {
|
||||
if args != nil {
|
||||
args["lang"] = APILanguage
|
||||
}
|
||||
|
||||
return &Request{ServiceMethod: serviceMethod, Args: args}
|
||||
}
|
||||
|
||||
// Do sends an API request and returns the API response.
|
||||
func (c *Client) Do(req Request) (*map[string]interface{}, error) {
|
||||
var resp Response
|
||||
err := c.RPCClient.Call(req.ServiceMethod, req.Args, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &resp.ResponseData, CheckResponse(&resp)
|
||||
}
|
||||
|
||||
func (r *ErrorResponse) Error() string {
|
||||
if r.Reason != "" {
|
||||
return fmt.Sprintf("(%d) %s. Reason: (%s) %s",
|
||||
r.Code, r.Message, r.ReasonCode, r.Reason)
|
||||
}
|
||||
return fmt.Sprintf("(%d) %s", r.Code, r.Message)
|
||||
}
|
||||
|
||||
// CheckResponse checks the API response for errors, and returns them if present.
|
||||
func CheckResponse(r *Response) error {
|
||||
if c := r.Code; c >= 1000 && c <= 1500 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &ErrorResponse{Code: r.Code, Message: r.Message, Reason: r.Reason, ReasonCode: r.ReasonCode}
|
||||
}
|
275
vendor/github.com/smueller18/goinwx/nameserver.go
generated
vendored
Normal file
275
vendor/github.com/smueller18/goinwx/nameserver.go
generated
vendored
Normal file
|
@ -0,0 +1,275 @@
|
|||
package goinwx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/structs"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
const (
|
||||
methodNameserverCheck = "nameserver.check"
|
||||
methodNameserverCreate = "nameserver.create"
|
||||
methodNameserverCreateRecord = "nameserver.createRecord"
|
||||
methodNameserverDelete = "nameserver.delete"
|
||||
methodNameserverDeleteRecord = "nameserver.deleteRecord"
|
||||
methodNameserverInfo = "nameserver.info"
|
||||
methodNameserverList = "nameserver.list"
|
||||
methodNameserverUpdate = "nameserver.update"
|
||||
methodNameserverUpdateRecord = "nameserver.updateRecord"
|
||||
)
|
||||
|
||||
type NameserverService interface {
|
||||
Check(domain string, nameservers []string) (*NameserverCheckResponse, error)
|
||||
Create(*NameserverCreateRequest) (int, error)
|
||||
Info(*NameserverInfoRequest) (*NamserverInfoResponse, error)
|
||||
List(domain string) (*NamserverListResponse, error)
|
||||
CreateRecord(*NameserverRecordRequest) (int, error)
|
||||
UpdateRecord(recId int, request *NameserverRecordRequest) error
|
||||
DeleteRecord(recId int) error
|
||||
FindRecordById(recId int) (*NameserverRecord, *NameserverDomain, error)
|
||||
}
|
||||
|
||||
type NameserverServiceOp struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
var _ NameserverService = &NameserverServiceOp{}
|
||||
|
||||
type NameserverCheckResponse struct {
|
||||
Details []string
|
||||
Status string
|
||||
}
|
||||
|
||||
type NameserverRecordRequest struct {
|
||||
RoId int `structs:"roId,omitempty"`
|
||||
Domain string `structs:"domain,omitempty"`
|
||||
Type string `structs:"type"`
|
||||
Content string `structs:"content"`
|
||||
Name string `structs:"name,omitempty"`
|
||||
Ttl int `structs:"ttl,omitempty"`
|
||||
Priority int `structs:"prio,omitempty"`
|
||||
UrlRedirectType string `structs:"urlRedirectType,omitempty"`
|
||||
UrlRedirectTitle string `structs:"urlRedirectTitle,omitempty"`
|
||||
UrlRedirectDescription string `structs:"urlRedirectDescription,omitempty"`
|
||||
UrlRedirectFavIcon string `structs:"urlRedirectFavIcon,omitempty"`
|
||||
UrlRedirectKeywords string `structs:"urlRedirectKeywords,omitempty"`
|
||||
}
|
||||
|
||||
type NameserverCreateRequest struct {
|
||||
Domain string `structs:"domain"`
|
||||
Type string `structs:"type"`
|
||||
Nameservers []string `structs:"ns,omitempty"`
|
||||
MasterIp string `structs:"masterIp,omitempty"`
|
||||
Web string `structs:"web,omitempty"`
|
||||
Mail string `structs:"mail,omitempty"`
|
||||
SoaEmail string `structs:"soaEmail,omitempty"`
|
||||
UrlRedirectType string `structs:"urlRedirectType,omitempty"`
|
||||
UrlRedirectTitle string `structs:"urlRedirectTitle,omitempty"`
|
||||
UrlRedirectDescription string `structs:"urlRedirectDescription,omitempty"`
|
||||
UrlRedirectFavIcon string `structs:"urlRedirectFavIcon,omitempty"`
|
||||
UrlRedirectKeywords string `structs:"urlRedirectKeywords,omitempty"`
|
||||
Testing bool `structs:"testing,omitempty"`
|
||||
}
|
||||
|
||||
type NameserverInfoRequest struct {
|
||||
Domain string `structs:"domain,omitempty"`
|
||||
RoId int `structs:"roId,omitempty"`
|
||||
RecordId int `structs:"recordId,omitempty"`
|
||||
Type string `structs:"type,omitempty"`
|
||||
Name string `structs:"name,omitempty"`
|
||||
Content string `structs:"content,omitempty"`
|
||||
Ttl int `structs:"ttl,omitempty"`
|
||||
Prio int `structs:"prio,omitempty"`
|
||||
}
|
||||
|
||||
type NamserverInfoResponse struct {
|
||||
RoId int
|
||||
Domain string
|
||||
Type string
|
||||
MasterIp string
|
||||
LastZoneCheck time.Time
|
||||
SlaveDns interface{}
|
||||
SOAserial string
|
||||
Count int
|
||||
Records []NameserverRecord `mapstructure:"record"`
|
||||
}
|
||||
|
||||
type NameserverRecord struct {
|
||||
Id int
|
||||
Name string
|
||||
Type string
|
||||
Content string
|
||||
Ttl int
|
||||
Prio int
|
||||
UrlRedirectType string
|
||||
UrlRedirectTitle string
|
||||
UrlRedirectDescription string
|
||||
UrlRedirectKeywords string
|
||||
UrlRedirectFavIcon string
|
||||
}
|
||||
|
||||
type NamserverListResponse struct {
|
||||
Count int
|
||||
Domains []NameserverDomain `mapstructure:"domains"`
|
||||
}
|
||||
|
||||
type NameserverDomain struct {
|
||||
RoId int `mapstructure:"roId"`
|
||||
Domain string `mapstructure:"domain"`
|
||||
Type string `mapstructure:"type"`
|
||||
MasterIp string `mapstructure:"masterIp"`
|
||||
Mail string `mapstructure:"mail"`
|
||||
Web string `mapstructure:"web"`
|
||||
Url string `mapstructure:"url"`
|
||||
Ipv4 string `mapstructure:"ipv4"`
|
||||
Ipv6 string `mapstructure:"ipv6"`
|
||||
}
|
||||
|
||||
func (s *NameserverServiceOp) Check(domain string, nameservers []string) (*NameserverCheckResponse, error) {
|
||||
req := s.client.NewRequest(methodNameserverCheck, map[string]interface{}{
|
||||
"domain": domain,
|
||||
"ns": nameservers,
|
||||
})
|
||||
|
||||
resp, err := s.client.Do(*req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result NameserverCheckResponse
|
||||
err = mapstructure.Decode(*resp, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (s *NameserverServiceOp) Info(request *NameserverInfoRequest) (*NamserverInfoResponse, error) {
|
||||
req := s.client.NewRequest(methodNameserverInfo, structs.Map(request))
|
||||
|
||||
resp, err := s.client.Do(*req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result NamserverInfoResponse
|
||||
err = mapstructure.Decode(*resp, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (s *NameserverServiceOp) List(domain string) (*NamserverListResponse, error) {
|
||||
requestMap := map[string]interface{}{
|
||||
"domain": "*",
|
||||
"wide": 2,
|
||||
}
|
||||
if domain != "" {
|
||||
requestMap["domain"] = domain
|
||||
}
|
||||
req := s.client.NewRequest(methodNameserverList, requestMap)
|
||||
|
||||
resp, err := s.client.Do(*req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result NamserverListResponse
|
||||
err = mapstructure.Decode(*resp, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (s *NameserverServiceOp) Create(request *NameserverCreateRequest) (int, error) {
|
||||
req := s.client.NewRequest(methodNameserverCreate, structs.Map(request))
|
||||
|
||||
resp, err := s.client.Do(*req)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var result map[string]int
|
||||
err = mapstructure.Decode(*resp, &result)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return result["roId"], nil
|
||||
}
|
||||
|
||||
func (s *NameserverServiceOp) CreateRecord(request *NameserverRecordRequest) (int, error) {
|
||||
req := s.client.NewRequest(methodNameserverCreateRecord, structs.Map(request))
|
||||
|
||||
resp, err := s.client.Do(*req)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var result map[string]int
|
||||
err = mapstructure.Decode(*resp, &result)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return result["id"], nil
|
||||
}
|
||||
|
||||
func (s *NameserverServiceOp) UpdateRecord(recId int, request *NameserverRecordRequest) error {
|
||||
if request == nil {
|
||||
return errors.New("Request can't be nil")
|
||||
}
|
||||
requestMap := structs.Map(request)
|
||||
requestMap["id"] = recId
|
||||
|
||||
req := s.client.NewRequest(methodNameserverUpdateRecord, requestMap)
|
||||
|
||||
_, err := s.client.Do(*req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *NameserverServiceOp) DeleteRecord(recId int) error {
|
||||
req := s.client.NewRequest(methodNameserverDeleteRecord, map[string]interface{}{
|
||||
"id": recId,
|
||||
})
|
||||
|
||||
_, err := s.client.Do(*req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *NameserverServiceOp) FindRecordById(recId int) (*NameserverRecord, *NameserverDomain, error) {
|
||||
listResp, err := s.client.Nameservers.List("")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for _, domainItem := range listResp.Domains {
|
||||
resp, err := s.client.Nameservers.Info(&NameserverInfoRequest{RoId: domainItem.RoId})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for _, record := range resp.Records {
|
||||
if record.Id == recId {
|
||||
return &record, &domainItem, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil, fmt.Errorf("couldn't find INWX Record for id %d", recId)
|
||||
|
||||
}
|
Loading…
Reference in a new issue