From c2ba58849111a03212428f4f0d0b6a1b18c4e6ab Mon Sep 17 00:00:00 2001 From: AsifNawaz-cnic Date: Tue, 13 Aug 2024 14:14:24 +0000 Subject: [PATCH] feat(centralnic reseller go-sdk): introducing CNR Go-SDK API Connector BREAKING CHANGE: We have deprecated hexonet go sdk and introduced centralnic reseller go sdk due to merger of Hexonet to CNR --- apiclient/apiclient.go | 60 ++--- apiclient/apiclient_test.go | 254 ++++++++---------- column/column.go | 5 +- response/response.go | 7 +- responsetranslator/responsetranslator.go | 63 +++-- responsetranslator/responsetranslator_test.go | 24 +- socketconfig/socketconfig.go | 35 --- 7 files changed, 188 insertions(+), 260 deletions(-) diff --git a/apiclient/apiclient.go b/apiclient/apiclient.go index 8a8e57f..df23933 100644 --- a/apiclient/apiclient.go +++ b/apiclient/apiclient.go @@ -27,14 +27,14 @@ import ( SC "github.com/centralnicgroup-opensource/rtldev-middleware-go-sdk/v4/socketconfig" ) -// ISPAPI_CONNECTION_URL_PROXY represents the url used for the high performance connection setup -const ISPAPI_CONNECTION_URL_PROXY = "http://127.0.0.1/api/call.cgi" //nolint +// CNR_CONNECTION_URL_PROXY represents the url used for the high performance connection setup +const CNR_CONNECTION_URL_PROXY = "http://127.0.0.1/api/call.cgi" //nolint -// ISPAPI_CONNECTION_URL_LIVE represents the url used for the default connection setup -const ISPAPI_CONNECTION_URL_LIVE = "https://api.ispapi.net/api/call.cgi" //nolint +// CNR_CONNECTION_URL_LIVE represents the url used for the default connection setup +const CNR_CONNECTION_URL_LIVE = "https://api.rrpproxy.net/api/call.cgi" //nolint -// ISPAPI_CONNECTION_URL_OTE represents the url used for the OT&E (demo system) connection setup -const ISPAPI_CONNECTION_URL_OTE = "https://api-ote.ispapi.net/api/call.cgi" //nolint +// CNR_CONNECTION_URL_OTE represents the url used for the OT&E (demo system) connection setup +const CNR_CONNECTION_URL_OTE = "https://api-ote.rrpproxy.net/api/call.cgi" //nolint var rtm = RTM.GetInstance() @@ -66,7 +66,7 @@ func NewAPIClient() *APIClient { cl := &APIClient{ debugMode: false, socketTimeout: 300 * time.Second, - socketURL: ISPAPI_CONNECTION_URL_LIVE, + socketURL: CNR_CONNECTION_URL_LIVE, socketConfig: SC.NewSocketConfig(), curlopts: map[string]string{}, ua: "", @@ -139,6 +139,12 @@ func (cl *APIClient) DisableDebugMode() *APIClient { return cl } +// UseHighPerformanceConnectionSetup to activate high performance conneciton setup +func (cl *APIClient) UseHighPerformanceConnectionSetup() *APIClient { + cl.SetURL(CNR_CONNECTION_URL_PROXY) + return cl +} + // GetPOSTData method to Serialize given command for POST request // including connection configuration data func (cl *APIClient) GetPOSTData(cmd map[string]string, secured ...bool) string { @@ -219,7 +225,6 @@ func (cl *APIClient) GetVersion() string { // 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 @@ -229,7 +234,6 @@ func (cl *APIClient) SaveSession(sessionobj map[string]interface{}) *APIClient { // 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 } @@ -252,12 +256,6 @@ func (cl *APIClient) SetSession(value string) *APIClient { 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) @@ -268,7 +266,7 @@ func (cl *APIClient) SetCredentials(uid string, pw string) *APIClient { // 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+":"+role, pw) } return cl.SetCredentials(uid, pw) } @@ -283,7 +281,7 @@ func (cl *APIClient) Login(params ...string) *R.Response { cl.SetOTP(otp) rr := cl.Request(map[string]interface{}{"COMMAND": "StartSession"}) if rr.IsSuccess() { - col := rr.GetColumn("SESSION") + col := rr.GetColumn("SESSIONID") if col != nil { cl.SetSession(col.GetData()[0]) } else { @@ -314,7 +312,7 @@ func (cl *APIClient) LoginExtended(params ...interface{}) *R.Response { } rr := cl.Request(cmd) if rr.IsSuccess() { - col := rr.GetColumn("SESSION") + col := rr.GetColumn("SESSIONID") if col != nil { cl.SetSession(col.GetData()[0]) } else { @@ -460,42 +458,22 @@ func (cl *APIClient) RequestAllResponsePages(cmd map[string]string) []R.Response 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 -} - -// UseHighPerformanceConnectionSetup to activate high performance conneciton setup -func (cl *APIClient) UseHighPerformanceConnectionSetup() *APIClient { - cl.SetURL(ISPAPI_CONNECTION_URL_PROXY) - return cl -} - // UseDefaultConnectionSetup to activate default conneciton setup (the default anyways) func (cl *APIClient) UseDefaultConnectionSetup() *APIClient { - cl.SetURL(ISPAPI_CONNECTION_URL_LIVE) + cl.SetURL(CNR_CONNECTION_URL_LIVE) return cl } // UseOTESystem method to set OT&E System for API communication func (cl *APIClient) UseOTESystem() *APIClient { - cl.SetURL(ISPAPI_CONNECTION_URL_OTE) - cl.socketConfig.SetSystemEntity("1234") + cl.SetURL(CNR_CONNECTION_URL_OTE) 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.SetURL(ISPAPI_CONNECTION_URL_LIVE) - cl.socketConfig.SetSystemEntity("54cd") + cl.SetURL(CNR_CONNECTION_URL_LIVE) return cl } diff --git a/apiclient/apiclient_test.go b/apiclient/apiclient_test.go index 0f1515b..ebda80f 100644 --- a/apiclient/apiclient_test.go +++ b/apiclient/apiclient_test.go @@ -31,7 +31,7 @@ func TestMain(m *testing.M) { } func TestGetPOSTData1(t *testing.T) { - validate := "s_entity=54cd&s_command=AUTH%3Dgwrgwqg%25%26%5C44t3%2A%0ACOMMAND%3DModifyDomain" + validate := "s_command=AUTH%3Dgwrgwqg%25%26%5C44t3%2A%0ACOMMAND%3DModifyDomain" enc := cl.GetPOSTData(map[string]string{ "COMMAND": "ModifyDomain", "AUTH": "gwrgwqg%&\\44t3*", @@ -42,12 +42,12 @@ func TestGetPOSTData1(t *testing.T) { } func TestGetPOSTDataSecured(t *testing.T) { - validate := "s_entity=54cd&s_login=test.user&s_pw=***&s_command=COMMAND%3DCheckAuthentication%0APASSWORD%3D%2A%2A%2A%0ASUBUSER%3Dtest.user" - cl.SetCredentials("test.user", "test.passw0rd") + validate := "s_login=qmtest%3Amiddleware&s_pw=***&s_command=COMMAND%3DCheckAuthentication%0APASSWORD%3D%2A%2A%2A%0ASUBUSER%3Dqmtest%3Amiddleware" + cl.SetCredentials(os.Getenv("CNR_TEST_USER"), os.Getenv("CNR_TEST_PASSWORD")) enc := cl.GetPOSTData(map[string]string{ "COMMAND": "CheckAuthentication", - "SUBUSER": "test.user", - "PASSWORD": "test.passw0rd", + "SUBUSER": os.Getenv("CNR_TEST_USER"), + "PASSWORD": os.Getenv("CNR_TEST_PASSWORD"), }, true) if strings.Compare(enc, validate) != 0 { t.Error(fmt.Printf("TestGetPOSTDataSecured: Expected encoding result not matching\n\n%s\n%s.", enc, validate)) @@ -64,7 +64,7 @@ func TestDisableDebugMode(_ *testing.T) { } func TestRequestFlattenCommand(t *testing.T) { - cl.SetCredentials("test.user", "test.passw0rd") + cl.SetCredentials(os.Getenv("CNR_TEST_USER"), os.Getenv("CNR_TEST_PASSWORD")) cl.UseOTESystem() r := cl.Request(map[string]interface{}{ "COMMAND": "CheckDomains", @@ -87,7 +87,7 @@ func TestRequestFlattenCommand(t *testing.T) { } func TestAutoIDNConvertCommand(t *testing.T) { - cl.SetCredentials("test.user", "test.passw0rd") + cl.SetCredentials(os.Getenv("CNR_TEST_USER"), os.Getenv("CNR_TEST_PASSWORD")) cl.UseOTESystem() r := cl.Request(map[string]interface{}{ "COMMAND": "CheckDomains", @@ -137,7 +137,7 @@ func TestGetSesssion2(t *testing.T) { func TestGetURL(t *testing.T) { url := cl.GetURL() - if strings.Compare(url, ISPAPI_CONNECTION_URL_LIVE) != 0 { + if strings.Compare(url, CNR_CONNECTION_URL_LIVE) != 0 { t.Error("TestGetURL: Expected url not matching.") } } @@ -172,11 +172,11 @@ func TestSetUserAgentModules(t *testing.T) { } func TestSetURL(t *testing.T) { - url := cl.SetURL(ISPAPI_CONNECTION_URL_PROXY).GetURL() - if strings.Compare(ISPAPI_CONNECTION_URL_PROXY, url) != 0 { + url := cl.SetURL(CNR_CONNECTION_URL_PROXY).GetURL() + if strings.Compare(CNR_CONNECTION_URL_PROXY, url) != 0 { t.Error("TestSetURL: Expected url not matching.") } - cl.SetURL(ISPAPI_CONNECTION_URL_LIVE) + cl.SetURL(CNR_CONNECTION_URL_LIVE) } func TestSetOTP1(t *testing.T) { @@ -184,7 +184,7 @@ func TestSetOTP1(t *testing.T) { tmp := cl.GetPOSTData(map[string]string{ "COMMAND": "StatusAccount", }) - if strings.Compare(tmp, "s_entity=54cd&s_otp=12345678&s_command=COMMAND%3DStatusAccount") != 0 { + if strings.Compare(tmp, "s_otp=12345678&s_command=COMMAND%3DStatusAccount") != 0 { t.Error("TestSetOTP1: Expected post data string not matching.") } } @@ -194,7 +194,7 @@ func TestSetOTP2(t *testing.T) { tmp := cl.GetPOSTData(map[string]string{ "COMMAND": "StatusAccount", }) - if strings.Compare(tmp, "s_entity=54cd&s_command=COMMAND%3DStatusAccount") != 0 { + if strings.Compare(tmp, "s_command=COMMAND%3DStatusAccount") != 0 { t.Error("TestSetOTP2: Expected post data string not matching.") } } @@ -204,7 +204,7 @@ func TestSetSession1(t *testing.T) { tmp := cl.GetPOSTData(map[string]string{ "COMMAND": "StatusAccount", }) - if strings.Compare(tmp, "s_entity=54cd&s_session=12345678&s_command=COMMAND%3DStatusAccount") != 0 { + if strings.Compare(tmp, "s_session=12345678&s_command=COMMAND%3DStatusAccount") != 0 { t.Error("TestSession1: Expected post data string not matching.") } } @@ -216,7 +216,7 @@ func TestSetSession2(t *testing.T) { tmp := cl.GetPOSTData(map[string]string{ "COMMAND": "StatusAccount", }) - if strings.Compare(tmp, "s_entity=54cd&s_session=12345678&s_command=COMMAND%3DStatusAccount") != 0 { + if strings.Compare(tmp, "s_session=12345678&s_command=COMMAND%3DStatusAccount") != 0 { t.Error("TestSession2: Expected post data string not matching.") } } @@ -226,7 +226,7 @@ func TestSetSession3(t *testing.T) { tmp := cl.GetPOSTData(map[string]string{ "COMMAND": "StatusAccount", }) - if strings.Compare(tmp, "s_entity=54cd&s_command=COMMAND%3DStatusAccount") != 0 { + if strings.Compare(tmp, "s_command=COMMAND%3DStatusAccount") != 0 { t.Error("TestSession3: Expected post data string not matching.") } } @@ -240,38 +240,18 @@ func TestSaveReuseSession(t *testing.T) { tmp := cl2.GetPOSTData(map[string]string{ "COMMAND": "StatusAccount", }) - if strings.Compare(tmp, "s_entity=54cd&s_session=12345678&s_command=COMMAND%3DStatusAccount") != 0 { + if strings.Compare(tmp, "s_session=12345678&s_command=COMMAND%3DStatusAccount") != 0 { t.Error("TestSaveReuseSession: Expected post data string not matching.") } cl.SetSession("") } -func TestSetRemoteIPAddress1(t *testing.T) { - cl.SetRemoteIPAddress("10.10.10.10") - tmp := cl.GetPOSTData(map[string]string{ - "COMMAND": "StatusAccount", - }) - if strings.Compare(tmp, "s_entity=54cd&s_remoteaddr=10.10.10.10&s_command=COMMAND%3DStatusAccount") != 0 { - t.Error("TestSetRemoteIPAddress1: Expected post data string not matching.") - } -} - -func TestSetRemoteIPAddress2(t *testing.T) { - cl.SetRemoteIPAddress("") - tmp := cl.GetPOSTData(map[string]string{ - "COMMAND": "StatusAccount", - }) - if strings.Compare(tmp, "s_entity=54cd&s_command=COMMAND%3DStatusAccount") != 0 { - t.Error("TestSetRemoteIPAddress2: Expected post data string not matching.") - } -} - func TestSetCredentials1(t *testing.T) { cl.SetCredentials("myaccountid", "mypassword") tmp := cl.GetPOSTData(map[string]string{ "COMMAND": "StatusAccount", }) - if strings.Compare(tmp, "s_entity=54cd&s_login=myaccountid&s_pw=mypassword&s_command=COMMAND%3DStatusAccount") != 0 { + if strings.Compare(tmp, "s_login=myaccountid&s_pw=mypassword&s_command=COMMAND%3DStatusAccount") != 0 { t.Error("TestSetCredentials1: Expected post data string not matching.") } } @@ -281,7 +261,7 @@ func TestSetCredentials2(t *testing.T) { tmp := cl.GetPOSTData(map[string]string{ "COMMAND": "StatusAccount", }) - if strings.Compare(tmp, "s_entity=54cd&s_command=COMMAND%3DStatusAccount") != 0 { + if strings.Compare(tmp, "s_command=COMMAND%3DStatusAccount") != 0 { t.Error("TestSetCredentials2: Expected post data string not matching.") } } @@ -291,7 +271,7 @@ func TestSetRoleCredentials1(t *testing.T) { tmp := cl.GetPOSTData(map[string]string{ "COMMAND": "StatusAccount", }) - if strings.Compare(tmp, "s_entity=54cd&s_login=myaccountid%21myroleid&s_pw=mypassword&s_command=COMMAND%3DStatusAccount") != 0 { + if strings.Compare(tmp, "s_login=myaccountid%3Amyroleid&s_pw=mypassword&s_command=COMMAND%3DStatusAccount") != 0 { t.Error("TestSetRoleCredentials1: Expected post data string not matching.") } } @@ -301,7 +281,7 @@ func TestSetRoleCredentials2(t *testing.T) { tmp := cl.GetPOSTData(map[string]string{ "COMMAND": "StatusAccount", }) - if strings.Compare(tmp, "s_entity=54cd&s_command=COMMAND%3DStatusAccount") != 0 { + if strings.Compare(tmp, "s_command=COMMAND%3DStatusAccount") != 0 { t.Error("TestSetRoleCredentials2: Expected post data string not matching.") } } @@ -327,7 +307,7 @@ func TestSetReferer(t *testing.T) { func TestUseHighPerformanceConnectionSetup(t *testing.T) { cl.UseHighPerformanceConnectionSetup() val := cl.GetURL() - if val != ISPAPI_CONNECTION_URL_PROXY { + if val != CNR_CONNECTION_URL_PROXY { t.Error("TestUseHighPerformanceConnectionSetup: couldn't activate high performance connection setup") } } @@ -335,34 +315,34 @@ func TestUseHighPerformanceConnectionSetup(t *testing.T) { func TestDefaultConnectionSetup(t *testing.T) { cl.UseDefaultConnectionSetup() val := cl.GetURL() - if val != ISPAPI_CONNECTION_URL_LIVE { + if val != CNR_CONNECTION_URL_LIVE { t.Error("TestDefaultConnectionSetup: couldn't activate default connection setup") } } -func TestLogin1(t *testing.T) { +func TestAccountStatus(t *testing.T) { cl.UseOTESystem() - cl.SetCredentials("test.user", "test.passw0rd") - cl.SetRemoteIPAddress("1.2.3.4") + cl.SetCredentials(os.Getenv("CNR_TEST_USER"), os.Getenv("CNR_TEST_PASSWORD")) cl.EnableDebugMode() - r := cl.Login() + cmd := map[string]interface{}{} + cmd["COMMAND"] = "StatusAccount" + r := cl.Request(cmd) + if r.GetDescription() == "Authorization failed" { + t.Error("TestAccountStatus: Please make sure correct credentials are provided") + } + if !r.IsSuccess() { - t.Error("TestLogin1: Expected response to be a success case.") + t.Error("TestAccountStatus: Expected response to be a success case.") } rec := r.GetRecord(0) if rec == nil { - t.Error("TestLogin1: Expected record not to be nil.") - } - d, err := rec.GetDataByKey("SESSION") - if err != nil || d == "" { - t.Error("TestLogin1: Expected session not to be empty.") + t.Error("TestAccountStatus: Expected record not to be nil.") } } /*func TestLogin2(t *testing.T) { cl.UseOTESystem() - cl.SetRoleCredentials("test.user", "testrole", "test.passw0rd") - cl.SetRemoteIPAddress("1.2.3.4") + cl.SetRoleCredentials(os.Getenv("CNR_TEST_USER"), "testrole", os.Getenv("CNR_TEST_PASSWORD")) r := cl.Login() if !r.IsSuccess() { t.Error("TestLogin2: Expected response to be a success case.") @@ -377,65 +357,63 @@ func TestLogin1(t *testing.T) { } }*/ -func TestLogin3(t *testing.T) { - cl.SetCredentials("test.user", "WRONGPASSWORD") - cl.SetRemoteIPAddress("1.2.3.4") - r := cl.Login() - if !r.IsError() { - t.Error("TestLogin3: Expected response to be an error case.") - } -} - -// validate against mocked API response [login failed; http timeout] // need mocking -// validate against mocked API response [login succeeded; no session returned] // need mocking - -func TestLoginExtended(t *testing.T) { - cl.UseOTESystem() - cl.SetCredentials("test.user", "test.passw0rd") - cl.SetRemoteIPAddress("1.2.3.4") - r := cl.LoginExtended(map[string]string{ - "TIMEOUT": "60", - }) - if !r.IsSuccess() { - t.Error("TestLoginExtended: Expected response to be a success case.") - } - rec := r.GetRecord(0) - if rec == nil { - t.Error("TestLoginExtended: Expected record not to be nil.") - } - d, err := rec.GetDataByKey("SESSION") - if err != nil && d == "" { - t.Error("TestLoginExtended: Expected session not to be empty.") - } -} - -func TestLogout1(t *testing.T) { - r := cl.Logout() - if !r.IsSuccess() { - t.Error("TestLogout1: Expected response to be a success case.") - } -} - -func TestLogout2(t *testing.T) { - tpl := R.NewResponse( - rtm.GetTemplate("login200"), - map[string]string{ - "COMMAND": "StartSession", - }, - nil, - ) - rec := tpl.GetRecord(0) - sessid, err := rec.GetDataByKey("SESSION") - if err != nil { - t.Error("TestLogout2: Expected not run into error.") - } - cl.EnableDebugMode() - cl.SetSession(sessid) - r := cl.Logout() - if !r.IsError() { - t.Error("TestLogout2: Expected response to be an error case.") - } -} +// func TestLogin3(t *testing.T) { +// cl.SetCredentials(os.Getenv("CNR_TEST_USER"), "WRONGPASSWORD") +// r := cl.Login() +// if !r.IsError() { +// t.Error("TestLogin3: Expected response to be an error case.") +// } +// } + +// // validate against mocked API response [login failed; http timeout] // need mocking +// // validate against mocked API response [login succeeded; no session returned] // need mocking + +// func TestLoginExtended(t *testing.T) { +// cl.UseOTESystem() +// cl.SetCredentials(os.Getenv("CNR_TEST_USER"), os.Getenv("CNR_TEST_PASSWORD")) +// r := cl.LoginExtended(map[string]string{ +// "TIMEOUT": "60", +// }) +// if !r.IsSuccess() { +// t.Error("TestLoginExtended: Expected response to be a success case.") +// } +// rec := r.GetRecord(0) +// if rec == nil { +// t.Error("TestLoginExtended: Expected record not to be nil.") +// } +// d, err := rec.GetDataByKey("SESSION") +// if err != nil && d == "" { +// t.Error("TestLoginExtended: Expected session not to be empty.") +// } +// } + +// func TestLogout1(t *testing.T) { +// r := cl.Logout() +// if !r.IsSuccess() { +// t.Error("TestLogout1: Expected response to be a success case.") +// } +// } + +// func TestLogout2(t *testing.T) { +// tpl := R.NewResponse( +// rtm.GetTemplate("login200"), +// map[string]string{ +// "COMMAND": "StartSession", +// }, +// nil, +// ) +// rec := tpl.GetRecord(0) +// sessid, err := rec.GetDataByKey("SESSION") +// if err != nil { +// t.Error("TestLogout2: Expected not run into error.") +// } +// cl.EnableDebugMode() +// cl.SetSession(sessid) +// r := cl.Logout() +// if !r.IsError() { +// t.Error("TestLogout2: Expected response to be an error case.") +// } +// } // validate against mocked API response [200 < r.statusCode > 299] // need mocking // validate against mocked API response [200 < r.statusCode > 299, no debug] // need mocking @@ -446,16 +424,11 @@ func TestRequestNextResponsePage1(t *testing.T) { // nolint: gocyclo map[string]string{ "COMMAND": "QueryDomainList", "LIMIT": "2", - "FIRST": "0", }, ) cl.UseOTESystem() - cl.SetRoleCredentials("test.user", "", "test.passw0rd") - cl.SetRemoteIPAddress("1.2.3.4") - nr := cl.Login() - if !nr.IsSuccess() { - t.Error("TestRequestNextResponsePage1: Expected login response to be a success case.") - } + cl.DisableDebugMode() + cl.SetCredentials(os.Getenv("CNR_TEST_USER"), os.Getenv("CNR_TEST_PASSWORD")) nr, err := cl.RequestNextResponsePage(r) if err != nil { t.Error(err) @@ -514,7 +487,6 @@ func TestRequestNextResponsePage2(t *testing.T) { } func TestRequestNextResponsePage3(t *testing.T) { // nolint: gocyclo - cl.DisableDebugMode() r := R.NewResponse( rtm.GetTemplate("listP0"), map[string]string{ @@ -573,22 +545,22 @@ func TestRequestAllResponsePages(t *testing.T) { } } -func TestSetUserView(t *testing.T) { - cl.SetUserView("hexotestman.com") - cmd := map[string]interface{}{} - cmd["COMMAND"] = "GetUserIndex" - r := cl.Request(cmd) - if !r.IsSuccess() { - t.Error("TestSetUserView: Expected response to be a success case.") - } -} - -func TestResetUserView(t *testing.T) { - cl.ResetUserView() - cmd := map[string]interface{}{} - cmd["COMMAND"] = "GetUserIndex" - r := cl.Request(cmd) - if !r.IsSuccess() { - t.Error("TestResetUserView: Expected response to be a success case.") - } -} +// func TestSetUserView(t *testing.T) { +// cl.SetUserView("hexotestman.com") +// cmd := map[string]interface{}{} +// cmd["COMMAND"] = "GetUserIndex" +// r := cl.Request(cmd) +// if !r.IsSuccess() { +// t.Error("TestSetUserView: Expected response to be a success case.") +// } +// } + +// func TestResetUserView(t *testing.T) { +// cl.ResetUserView() +// cmd := map[string]interface{}{} +// cmd["COMMAND"] = "GetUserIndex" +// r := cl.Request(cmd) +// if !r.IsSuccess() { +// t.Error("TestResetUserView: Expected response to be a success case.") +// } +// } diff --git a/column/column.go b/column/column.go index e68fdac..187af0e 100644 --- a/column/column.go +++ b/column/column.go @@ -32,6 +32,9 @@ func (c *Column) GetKey() string { // GetData method to return the column data func (c *Column) GetData() []string { + if c == nil { + return nil + } return c.data } @@ -40,7 +43,7 @@ func (c *Column) GetDataByIndex(idx int) (string, error) { if c.hasDataIndex(idx) { return c.data[idx], nil } - return "", errors.New("Index not found") + return "", errors.New("index not found") } // hasDataIndex method to check if the given data index exists diff --git a/response/response.go b/response/response.go index 2de1986..1a8e4cd 100644 --- a/response/response.go +++ b/response/response.go @@ -472,14 +472,15 @@ func (r *Response) RewindRecordList() *Response { return r } -// hasColumn method to check if the given column exists in column list +// hasColumn method to check if the given column exists in column list (case-insensitive) func (r *Response) hasColumn(key string) (int, bool) { + lowerKey := strings.ToLower(key) for i, k := range r.columnkeys { - if k == key { + if strings.ToLower(k) == lowerKey { return i, true } } - return 0, false + return -1, false } // hasCurrentRecord method to check if the record on current record index exists diff --git a/responsetranslator/responsetranslator.go b/responsetranslator/responsetranslator.go index 488a64f..d76e8fb 100644 --- a/responsetranslator/responsetranslator.go +++ b/responsetranslator/responsetranslator.go @@ -17,26 +17,17 @@ type ResponseTranslator struct { } var descriptionRegexMap = map[string]string{ - // HX - "Authorization failed; Operation forbidden by ACL": "Authorization failed; Used Command `{COMMAND}` not white-listed by your Access Control List", - "Request is not available; DOMAIN TRANSFER IS PROHIBITED BY STATUS (clientTransferProhibited)/WRONG AUTH": "This Domain is locked and the given Authorization Code is wrong. Initiating a Transfer is therefore impossible.", - "Request is not available; DOMAIN TRANSFER IS PROHIBITED BY STATUS (clientTransferProhibited)": "This Domain is locked. Initiating a Transfer is therefore impossible.", - "Request is not available; DOMAIN TRANSFER IS PROHIBITED BY STATUS (requested)": "Registration of this Domain Name has not yet completed. Initiating a Transfer is therefore impossible.", - "Request is not available; DOMAIN TRANSFER IS PROHIBITED BY STATUS (requestedcreate)": "Registration of this Domain Name has not yet completed. Initiating a Transfer is therefore impossible.", - "Request is not available; DOMAIN TRANSFER IS PROHIBITED BY STATUS (requesteddelete)": "Deletion of this Domain Name has been requested. Initiating a Transfer is therefore impossible.", - "Request is not available; DOMAIN TRANSFER IS PROHIBITED BY STATUS (pendingdelete)": "Deletion of this Domain Name is pending. Initiating a Transfer is therefore impossible.", - "Request is not available; DOMAIN TRANSFER IS PROHIBITED BY WRONG AUTH": "The given Authorization Code is wrong. Initiating a Transfer is therefore impossible.", - "Request is not available; DOMAIN TRANSFER IS PROHIBITED BY AGE OF THE DOMAIN": "This Domain Name is within 60 days of initial registration. Initiating a Transfer is therefore impossible.", - "Attribute value is not unique; DOMAIN is already assigned to your account": "You cannot transfer a domain that is already on your account at the registrar's system.", + // HX | CNR + "Authorization failed; Operation forbidden by ACL": "Authorization failed; Used Command `{COMMAND}` not white-listed by your Access Control List", // CNR + "Domain status does not allow for operation": "This Domain is locked. Initiating a Transfer is therefore impossible.", + "Authorization failed [Invalid authorization information]": "The given Authorization Code is wrong. Initiating a Transfer is therefore impossible.", "Missing required attribute; premium domain name. please provide required parameters": "Confirm the Premium pricing by providing the necessary premium domain price data.", } var descriptionRegexMapSkipQuote = map[string]string{ - // HX - `Invalid attribute value syntax; resource record \[(.+)\]`: "Invalid Syntax for DNSZone Resource Record: $1", - `Missing required attribute; CLASS(?:=| \[MUST BE )PREMIUM_([\w\+]+)[\s\]]`: "Confirm the Premium pricing by providing the parameter CLASS with the value PREMIUM_$1.", - `Syntax error in Parameter DOMAIN \((.+)\)`: "The Domain Name $1 is invalid.", + // all cases goes here that need translation e.g. + // example case: `Missing required attribute; CLASS(?:=| \[MUST BE )PREMIUM_([\w\+]+)[\s\]]`: "Confirm the Premium pricing by providing the parameter CLASS with the value PREMIUM_$1.", } // Translate function for plain api response @@ -91,6 +82,7 @@ func Translate(raw string, cmd map[string]string, phs ...map[string]string) stri // Escape the regex pattern and attempt to find a match escapedRegex := regexp.QuoteMeta(regex) data = FindMatch(escapedRegex, newraw, val, cmd, ph) + // If a match is found, exit the inner loop if len(data) > 0 { newraw = data @@ -118,16 +110,18 @@ func FindMatch(regex string, newraw string, val string, cmd map[string]string, p // NOTE: we match if the description starts with the given description // it would also match if it is followed by additional text ret := "" - qregex := regexp.MustCompile("(?i)description\\s*=\\s*" + regex + "([^\\r\\n]+)?") + qregex := regexp.MustCompile(`(?i)description[\s]*=[\s]*` + regex + `([^\r\n]+)?`) if qregex.FindString(newraw) != "" { - // If "COMMAND" exists in cmd, replace "{COMMAND}" in val - myval, ok := cmd["COMMAND"] - if ok { - val = strings.ReplaceAll(val, "{COMMAND}", myval) + // Replace any placeholders dynamically in val using cmd and ph maps + for key, replaceVal := range cmd { + val = strings.ReplaceAll(val, "{"+key+"}", replaceVal) + } + for key, replaceVal := range ph { + val = strings.ReplaceAll(val, "{"+key+"}", replaceVal) } - // If $newraw matches $qregex, replace with "description=" . $val + // If $newraw matches $qregex, replace with "description=" + val tmp := qregex.ReplaceAllString(newraw, "description="+val) if newraw != tmp { ret = tmp @@ -139,14 +133,31 @@ func FindMatch(regex string, newraw string, val string, cmd map[string]string, p return ret } - // Generic replacing of placeholder vars + // Compile the regular expression to match placeholders vregex := regexp.MustCompile(`\{[^}]+\}`) - if vregex.FindString(ret) != "" { - for tkey, tval := range ph { - ret = strings.ReplaceAll(ret, "{"+tkey+"}", tval) + + // Keep track of whether any replacements were made + replacementsMade := false + + // Replace placeholders with corresponding values from the map + newret := ret + if ret == "" { + newret = newraw + } + newret = vregex.ReplaceAllStringFunc(newret, func(match string) string { + placeholder := match[1 : len(match)-1] // Extract the placeholder name + + // Check if the placeholder exists in the map + if value, exists := ph[placeholder]; exists { + replacementsMade = true + return value } + return "" // Remove placeholder if not found + }) - ret = vregex.ReplaceAllString(ret, "") + // Return an newret if replacements were made + if replacementsMade { + return newret } return ret diff --git a/responsetranslator/responsetranslator_test.go b/responsetranslator/responsetranslator_test.go index 45f227b..acdb51d 100644 --- a/responsetranslator/responsetranslator_test.go +++ b/responsetranslator/responsetranslator_test.go @@ -9,7 +9,7 @@ import ( ) func TestTranslate(t *testing.T) { - cmd := map[string]string{"COMMAND": "CheckDomainTransfer", "DOMAIN": "my–domain.com", "AUTH": "blablabla"} + cmd := map[string]string{"COMMAND": "CheckDomainTransfer", "DOMAIN": "google.com", "AUTH": "blablabla"} // Test ACL error translation t.Run("ACLTranslation", func(t *testing.T) { @@ -21,22 +21,16 @@ func TestTranslate(t *testing.T) { Hash: rp.Parse(newraw), } h := r.GetHash() + if h["DESCRIPTION"] != expected { t.Errorf("Expected: %s, got: %s", expected, h["DESCRIPTION"]) } }) - // Test CheckDomainTransfer translations t.Run("CheckDomainTransferTranslation", func(t *testing.T) { testCases := map[string]string{ - // Add more test cases as needed - "Request is not available; DOMAIN TRANSFER IS PROHIBITED BY STATUS (clientTransferProhibited)": "This Domain is locked. Initiating a Transfer is therefore impossible.", - "Request is not available; DOMAIN TRANSFER IS PROHIBITED BY STATUS (requested)": "Registration of this Domain Name has not yet completed. Initiating a Transfer is therefore impossible.", - "Request is not available; DOMAIN TRANSFER IS PROHIBITED BY STATUS (requestedcreate)": "Registration of this Domain Name has not yet completed. Initiating a Transfer is therefore impossible.", - "Request is not available; DOMAIN TRANSFER IS PROHIBITED BY STATUS (requesteddelete)": "Deletion of this Domain Name has been requested. Initiating a Transfer is therefore impossible.", - "Request is not available; DOMAIN TRANSFER IS PROHIBITED BY STATUS (pendingdelete)": "Deletion of this Domain Name is pending. Initiating a Transfer is therefore impossible.", - "Request is not available; DOMAIN TRANSFER IS PROHIBITED BY WRONG AUTH": "The given Authorization Code is wrong. Initiating a Transfer is therefore impossible.", - "Request is not available; DOMAIN TRANSFER IS PROHIBITED BY AGE OF THE DOMAIN": "This Domain Name is within 60 days of initial registration. Initiating a Transfer is therefore impossible.", + "Domain status does not allow for operation": "This Domain is locked. Initiating a Transfer is therefore impossible.", + "Authorization failed [Invalid authorization information]": "The given Authorization Code is wrong. Initiating a Transfer is therefore impossible.", } for input, expected := range testCases { @@ -47,6 +41,7 @@ func TestTranslate(t *testing.T) { Hash: rp.Parse(newraw), } h := r.GetHash() + if h["DESCRIPTION"] != expected { t.Errorf("Expected: %s, got: %s", expected, h["DESCRIPTION"]) } @@ -57,9 +52,8 @@ func TestTranslate(t *testing.T) { t.Run("Translate", func(t *testing.T) { testCases := map[string]string{ // Add more test cases as needed - "[RESPONSE]\r\ncode=219\r\ndescription=Request is not available; DOMAIN TRANSFER IS PROHIBITED BY STATUS (clientTransferProhibited)\r\nEOF\r\n": "This Domain is locked. Initiating a Transfer is therefore impossible.", - "[RESPONSE]\r\ncode=505\r\ndescription=Invalid attribute value syntax; resource record [213123 A 1.2.4.5asdfa]\r\nEOF\r\n": "Invalid Syntax for DNSZone Resource Record: 213123 A 1.2.4.5asdfa", - "[RESPONSE]\r\ncode=505\r\ndescription=Syntax error in Parameter DOMAIN (my–domain.de)\r\nEOF\r\n": "The Domain Name my–domain.de is invalid.", + "[RESPONSE]\r\ncode=505\r\ndescription=Invalid attribute value syntax; resource record [213123 A 1.2.4.5asdfa]\r\nEOF\r\n": "Invalid Syntax for DNSZone Resource Record: 213123 A 1.2.4.5asdfa", + "[RESPONSE]\r\ncode=505\r\ndescription=Syntax error in Parameter DOMAIN (my–domain.de)\r\nEOF\r\n": "The Domain Name my–domain.de is invalid.", } for input, expected := range testCases { @@ -69,6 +63,10 @@ func TestTranslate(t *testing.T) { Hash: rp.Parse(newraw), } h := r.GetHash() + + // Debug output + //fmt.Printf("Input: %s\nExpected: %s\nGot: %s\n", input, expected, h["DESCRIPTION"]) + if h["DESCRIPTION"] != expected { t.Errorf("Expected: %s, got: %s", expected, h["DESCRIPTION"]) } diff --git a/socketconfig/socketconfig.go b/socketconfig/socketconfig.go index 2f2c39b..cb4ce24 100644 --- a/socketconfig/socketconfig.go +++ b/socketconfig/socketconfig.go @@ -13,11 +13,9 @@ import ( // SocketConfig is a struct representing connection settings used as POST data for http request against the insanely fast HEXONET backend API. type SocketConfig struct { - entity string login string otp string pw string - remoteaddr string session string user string } @@ -25,11 +23,9 @@ type SocketConfig struct { // NewSocketConfig represents the constructor for struct SocketConfig. func NewSocketConfig() *SocketConfig { sc := &SocketConfig{ - entity: "", login: "", otp: "", pw: "", - remoteaddr: "", session: "", user: "", } @@ -40,12 +36,6 @@ func NewSocketConfig() *SocketConfig { // POST request of type "application/x-www-form-urlencoded" func (s *SocketConfig) GetPOSTData() string { var tmp strings.Builder - if len(s.entity) > 0 { - tmp.WriteString(url.QueryEscape("s_entity")) - tmp.WriteString("=") - tmp.WriteString(url.QueryEscape(s.entity)) - tmp.WriteString("&") - } if len(s.login) > 0 { tmp.WriteString(url.QueryEscape("s_login")) tmp.WriteString("=") @@ -64,12 +54,6 @@ func (s *SocketConfig) GetPOSTData() string { tmp.WriteString(url.QueryEscape(s.pw)) tmp.WriteString("&") } - if len(s.remoteaddr) > 0 { - tmp.WriteString(url.QueryEscape("s_remoteaddr")) - tmp.WriteString("=") - tmp.WriteString(url.QueryEscape(s.remoteaddr)) - tmp.WriteString("&") - } if len(s.session) > 0 { tmp.WriteString(url.QueryEscape("s_session")) tmp.WriteString("=") @@ -90,11 +74,6 @@ func (s *SocketConfig) GetSession() string { return s.session } -// GetSystemEntity method to return the API system entity currently in use. -func (s *SocketConfig) GetSystemEntity() string { - return s.entity -} - // SetLogin method to set username to use for api communication func (s *SocketConfig) SetLogin(value string) *SocketConfig { s.session = "" @@ -116,14 +95,6 @@ func (s *SocketConfig) SetPassword(value string) *SocketConfig { return s } -// SetRemoteAddress method to set remote ip address to be submitted to the HEXONET API. -// This ip address is being considered when you have ip filter settings activated. -// To reset this, simply provide an empty string as parameter. -func (s *SocketConfig) SetRemoteAddress(value string) *SocketConfig { - s.remoteaddr = value - return s -} - // SetSession method to set a API session id to use for api communication instead of credentials // which is basically required in case you plan to use session based communication or if you want to use 2FA func (s *SocketConfig) SetSession(sessionid string) *SocketConfig { @@ -134,12 +105,6 @@ func (s *SocketConfig) SetSession(sessionid string) *SocketConfig { return s } -// SetSystemEntity method to set the system to use e.g. 1234 -> OT&E System, 54cd -> LIVE System -func (s *SocketConfig) SetSystemEntity(value string) *SocketConfig { - s.entity = value - return s -} - // SetUser method to set an user account (must be subuser account of your login user) to use for API communication // use this if you want to make changes on that subuser account or if you want to have his data view func (s *SocketConfig) SetUser(username string) *SocketConfig {