1
0
mirror of https://github.com/StackExchange/dnscontrol.git synced 2024-05-11 05:55:12 +00:00
Files
stackexchange-dnscontrol/vendor/github.com/hexonet/go-sdk/apiclient/apiclient.go
Joel Margolis 1232c17293 HEXONET: Update hexonet-sdk to v2.2.3+incompatible (#662)
* Update hexonet-sdk to v2.2.3+incompatible

* Update vendor

* Go mod tidy
2020-02-27 13:04:17 -05:00

405 lines
11 KiB
Go

// Copyright (c) 2018 Kai Schwarz (HEXONET GmbH). All rights reserved.
//
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE.md file.
// Package apiclient contains all you need to communicate with the insanely fast HEXONET backend API.
package apiclient
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"runtime"
"sort"
"strings"
"time"
R "github.com/hexonet/go-sdk/response"
RTM "github.com/hexonet/go-sdk/responsetemplatemanager"
SC "github.com/hexonet/go-sdk/socketconfig"
)
var rtm = RTM.GetInstance()
// APIClient is the entry point class for communicating with the insanely fast HEXONET backend api.
// It allows two ways of communication:
// * session based communication
// * sessionless communication
//
// A session based communication makes sense in case you use it to
// build your own frontend on top. It allows also to use 2FA
// (2 Factor Auth) by providing "otp" in the config parameter of
// the login method.
// A sessionless communication makes sense in case you do not need
// to care about the above and you have just to request some commands.
//
// Possible commands can be found at https://github.com/hexonet/hexonet-api-documentation/tree/master/API
type APIClient struct {
socketTimeout time.Duration
socketURL string
socketConfig *SC.SocketConfig
debugMode bool
ua string
}
// NewAPIClient represents the constructor for struct APIClient.
func NewAPIClient() *APIClient {
cl := &APIClient{
debugMode: false,
socketTimeout: 300 * time.Second,
socketURL: "https://api.ispapi.net/api/call.cgi",
socketConfig: SC.NewSocketConfig(),
ua: "",
}
cl.UseLIVESystem()
return cl
}
// EnableDebugMode method to enable Debug Output to logger
func (cl *APIClient) EnableDebugMode() *APIClient {
cl.debugMode = true
return cl
}
// DisableDebugMode method to disable Debug Output to logger
func (cl *APIClient) DisableDebugMode() *APIClient {
cl.debugMode = false
return cl
}
// GetPOSTData method to Serialize given command for POST request
// including connection configuration data
func (cl *APIClient) GetPOSTData(cmd map[string]string) string {
data := cl.socketConfig.GetPOSTData()
var tmp strings.Builder
keys := []string{}
for key := range cmd {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
val := cmd[key]
tmp.WriteString(key)
tmp.WriteString("=")
val = strings.Replace(val, "\r", "", -1)
val = strings.Replace(val, "\n", "", -1)
tmp.WriteString(val)
tmp.WriteString("\n")
}
str := tmp.String()
str = str[:len(str)-1] //remove \n at end
return strings.Join([]string{
data,
url.QueryEscape("s_command"),
"=",
url.QueryEscape(str),
}, "")
}
// GetSession method to get the API Session that is currently set
func (cl *APIClient) GetSession() (string, error) {
sessid := cl.socketConfig.GetSession()
if len(sessid) == 0 {
return "", errors.New("Could not find an active session")
}
return sessid, nil
}
// GetURL method to get the API connection url that is currently set
func (cl *APIClient) GetURL() string {
return cl.socketURL
}
// SetUserAgent method to customize user-agent header (useful for tools that use our SDK)
func (cl *APIClient) SetUserAgent(str string, rv string) *APIClient {
cl.ua = str + " (" + runtime.GOOS + "; " + runtime.GOARCH + "; rv:" + rv + ") go-sdk/" + cl.GetVersion() + " go/" + runtime.Version()
return cl
}
// GetUserAgent method to return the user agent string
func (cl *APIClient) GetUserAgent() string {
if len(cl.ua) == 0 {
cl.ua = "GO-SDK (" + runtime.GOOS + "; " + runtime.GOARCH + "; rv:" + cl.GetVersion() + ") go/" + runtime.Version()
}
return cl.ua
}
// GetVersion method to get current module version
func (cl *APIClient) GetVersion() string {
return "2.2.3"
}
// SaveSession method to apply data to a session for later reuse
// Please save/update that map into user session
func (cl *APIClient) SaveSession(sessionobj map[string]interface{}) *APIClient {
sessionobj["socketcfg"] = map[string]string{
"entity": cl.socketConfig.GetSystemEntity(),
"session": cl.socketConfig.GetSession(),
}
return cl
}
// ReuseSession method to reuse given configuration out of a user session
// to rebuild and reuse connection settings
func (cl *APIClient) ReuseSession(sessionobj map[string]interface{}) *APIClient {
cfg := sessionobj["socketcfg"].(map[string]string)
cl.socketConfig.SetSystemEntity(cfg["entity"])
cl.SetSession(cfg["session"])
return cl
}
// SetURL method to set another connection url to be used for API communication
func (cl *APIClient) SetURL(value string) *APIClient {
cl.socketURL = value
return cl
}
// SetOTP method to set one time password to be used for API communication
func (cl *APIClient) SetOTP(value string) *APIClient {
cl.socketConfig.SetOTP(value)
return cl
}
// SetSession method to set an API session id to be used for API communication
func (cl *APIClient) SetSession(value string) *APIClient {
cl.socketConfig.SetSession(value)
return cl
}
// SetRemoteIPAddress method to set an Remote IP Address to be used for API communication
func (cl *APIClient) SetRemoteIPAddress(value string) *APIClient {
cl.socketConfig.SetRemoteAddress(value)
return cl
}
// SetCredentials method to set Credentials to be used for API communication
func (cl *APIClient) SetCredentials(uid string, pw string) *APIClient {
cl.socketConfig.SetLogin(uid)
cl.socketConfig.SetPassword(pw)
return cl
}
// SetRoleCredentials method to set Role User Credentials to be used for API communication
func (cl *APIClient) SetRoleCredentials(uid string, role string, pw string) *APIClient {
if len(role) > 0 {
return cl.SetCredentials(uid+"!"+role, pw)
}
return cl.SetCredentials(uid, pw)
}
// Login method to perform API login to start session-based communication
// 1st parameter: one time password
func (cl *APIClient) Login(params ...string) *R.Response {
otp := ""
if len(params) > 0 {
otp = params[0]
}
cl.SetOTP(otp)
rr := cl.Request(map[string]string{"COMMAND": "StartSession"})
if rr.IsSuccess() {
col := rr.GetColumn("SESSION")
if col != nil {
cl.SetSession(col.GetData()[0])
} else {
cl.SetSession("")
}
}
return rr
}
// LoginExtended method to perform API login to start session-based communication.
// 1st parameter: map of additional command parameters
// 2nd parameter: one time password
func (cl *APIClient) LoginExtended(params ...interface{}) *R.Response {
otp := ""
parameters := map[string]string{}
if len(params) == 2 {
otp = params[1].(string)
}
cl.SetOTP(otp)
if len(params) > 0 {
parameters = params[0].(map[string]string)
}
cmd := map[string]string{
"COMMAND": "StartSession",
}
for k, v := range parameters {
cmd[k] = v
}
rr := cl.Request(cmd)
if rr.IsSuccess() {
col := rr.GetColumn("SESSION")
if col != nil {
cl.SetSession(col.GetData()[0])
} else {
cl.SetSession("")
}
}
return rr
}
// Logout method to perform API logout to close API session in use
func (cl *APIClient) Logout() *R.Response {
rr := cl.Request(map[string]string{
"COMMAND": "EndSession",
})
if rr.IsSuccess() {
cl.SetSession("")
}
return rr
}
// Request method to perform API request using the given command
func (cl *APIClient) Request(cmd map[string]string) *R.Response {
data := cl.GetPOSTData(cmd)
client := &http.Client{
Timeout: cl.socketTimeout,
}
req, err := http.NewRequest("POST", cl.socketURL, strings.NewReader(data))
if err != nil {
tpl := rtm.GetTemplate("httperror").GetPlain()
r := R.NewResponse(tpl, cmd)
if cl.debugMode {
j, _ := json.Marshal(cmd)
fmt.Printf("%s\n", j)
fmt.Println("POST: " + data)
fmt.Println("HTTP communication failed: " + err.Error())
fmt.Println(r.GetPlain())
}
return r
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Expect", "")
req.Header.Add("User-Agent", cl.GetUserAgent())
resp, err2 := client.Do(req)
if err2 != nil {
tpl := rtm.GetTemplate("httperror").GetPlain()
r := R.NewResponse(tpl, cmd)
if cl.debugMode {
j, _ := json.Marshal(cmd)
fmt.Printf("%s\n", j)
fmt.Println("POST: " + data)
fmt.Println("HTTP communication failed: " + err2.Error())
fmt.Println(r.GetPlain())
}
return r
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
response, err := ioutil.ReadAll(resp.Body)
if err != nil {
tpl := rtm.GetTemplate("httperror").GetPlain()
r := R.NewResponse(tpl, cmd)
if cl.debugMode {
j, _ := json.Marshal(cmd)
fmt.Printf("%s\n", j)
fmt.Println("POST: " + data)
fmt.Println("HTTP communication failed: " + err.Error())
fmt.Println(r.GetPlain())
}
return r
}
r := R.NewResponse(string(response), cmd)
if cl.debugMode {
j, _ := json.Marshal(cmd)
fmt.Printf("%s\n", j)
fmt.Println("POST: " + data)
fmt.Println(r.GetPlain())
}
return r
}
tpl := rtm.GetTemplate("httperror").GetPlain()
r := R.NewResponse(tpl, cmd)
if cl.debugMode {
j, _ := json.Marshal(cmd)
fmt.Printf("%s\n", j)
fmt.Println("POST: " + data)
fmt.Println(r.GetPlain())
}
return r
}
// RequestNextResponsePage method to request the next page of list entries for the current list query
// Useful for lists
func (cl *APIClient) RequestNextResponsePage(rr *R.Response) (*R.Response, error) {
mycmd := cl.toUpperCaseKeys(rr.GetCommand())
if _, ok := mycmd["LAST"]; ok {
return nil, errors.New("Parameter LAST in use. Please remove it to avoid issues in requestNextPage")
}
first := 0
if v, ok := mycmd["FIRST"]; ok {
first, _ = fmt.Sscan("%s", v)
}
total := rr.GetRecordsTotalCount()
limit := rr.GetRecordsLimitation()
first += limit
if first < total {
mycmd["FIRST"] = fmt.Sprintf("%d", first)
mycmd["LIMIT"] = fmt.Sprintf("%d", limit)
return cl.Request(mycmd), nil
}
return nil, errors.New("Could not find further existing pages")
}
// RequestAllResponsePages method to request all pages/entries for the given query command
// Use this method with caution as it requests all list data until done.
func (cl *APIClient) RequestAllResponsePages(cmd map[string]string) []R.Response {
var err error
responses := []R.Response{}
mycmd := map[string]string{
"FIRST": "0",
}
for k, v := range cmd {
mycmd[k] = v
}
rr := cl.Request(mycmd)
tmp := rr
for {
responses = append(responses, *tmp)
tmp, err = cl.RequestNextResponsePage(tmp)
if err != nil {
break
}
}
return responses
}
// SetUserView method to set a data view to a given subuser
func (cl *APIClient) SetUserView(uid string) *APIClient {
cl.socketConfig.SetUser(uid)
return cl
}
// ResetUserView method to reset data view back from subuser to user
func (cl *APIClient) ResetUserView() *APIClient {
cl.socketConfig.SetUser("")
return cl
}
// UseOTESystem method to set OT&E System for API communication
func (cl *APIClient) UseOTESystem() *APIClient {
cl.socketConfig.SetSystemEntity("1234")
return cl
}
// UseLIVESystem method to set LIVE System for API communication
// Usage of LIVE System is active by default.
func (cl *APIClient) UseLIVESystem() *APIClient {
cl.socketConfig.SetSystemEntity("54cd")
return cl
}
// toUpperCaseKeys method to translate all command parameter names to uppercase
func (cl *APIClient) toUpperCaseKeys(cmd map[string]string) map[string]string {
newcmd := map[string]string{}
for k, v := range cmd {
newcmd[strings.ToUpper(k)] = v
}
return newcmd
}