Skip to content

Commit 8aebc72

Browse files
sleepdefic1tfaustbrian
authored andcommitted
feat: send transactions (#64)
* feat: TX Send - add 'Transactions::send()' method. - add 'Transactions::send()' Paths method. - add mock test for 'send' method. - add test for 'send' Path method. * fix: remove trailing slash * fix: remove trailing slash from tests * fix: correct jsonTransaction string - updates to correct json example for tx post/"send" body. * fix: set content-type on post * test: add json post test
1 parent d714a37 commit 8aebc72

File tree

10 files changed

+129
-4
lines changed

10 files changed

+129
-4
lines changed

src/api/paths.cpp

+13-2
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,19 @@ std::pair<std::string, std::string> Ark::Client::API::Paths::Transactions::searc
227227
return {uri, parameterBuffer.c_str()};
228228
}
229229

230+
/***/
231+
232+
std::pair<std::string, std::string> Ark::Client::API::Paths::Transactions::send(Host& newHost, std::string& jsonTransaction) {
233+
char uri[96] = {};
234+
snprintf(
235+
uri,
236+
sizeof(uri),
237+
"%s%s",
238+
newHost.toString().c_str(),
239+
Ark::Client::API::Paths::Transactions::base());
240+
return {uri, jsonTransaction.c_str()};
241+
}
242+
230243
/******/
231244

232245
/**
@@ -345,5 +358,3 @@ std::pair<std::string, std::string> Ark::Client::API::Paths::Wallets::search(
345358
}
346359
return {uri, parameterBuffer.c_str()};
347360
}
348-
349-
/***/

src/api/transactions/transactions.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,11 @@ std::string Ark::Client::API::Transactions::search(const std::map<std::string, s
3636
const auto searchPathPair = Ark::Client::API::Paths::Transactions::search(this->host_, bodyParameters, limit, page);
3737
return http_->post(searchPathPair.first.c_str(), searchPathPair.second.c_str());
3838
}
39+
40+
/***/
41+
42+
std::string Ark::Client::API::Transactions::send(std::string& jsonTransaction) {
43+
const auto pathPair = Ark::Client::API::Paths::Transactions::send(this->host_, jsonTransaction);
44+
return http_->post(pathPair.first.c_str(), pathPair.second.c_str());
45+
};
46+

src/http/iot/http.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ class PlatformHTTP : public AbstractHTTP {
8585
httpClient.setReuse(true);
8686
httpClient.setTimeout(3000);
8787
httpClient.begin(toHttpStr(request).c_str());
88-
httpClient.addHeader("Content-Type", "application/x-www-form-urlencoded");
88+
(body[0] == '{') // set the header content-type
89+
? httpClient.addHeader("Content-Type", "application/json")
90+
: httpClient.addHeader("Content-Type", "application/x-www-form-urlencoded");
8991
httpClient.POST(body);
9092
return httpClient.getString().c_str();
9193
}

src/http/os/http.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ class PlatformHTTP : public AbstractHTTP {
6969
curl_easy_setopt(curl, CURLOPT_URL, request); // Set the URL that is about to receive our POST
7070
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body); // Now specify the POST json data ex: "username=baldninja"
7171

72+
/* set the header content-type */
73+
curl_slist *header_list = nullptr;
74+
header_list = (body[0] == '{')
75+
? curl_slist_append(header_list, "Content-Type: application/json")
76+
: curl_slist_append(header_list, "Content-Type: application/x-www-form-urlencoded");
77+
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
78+
7279
/* skip https verification */
7380
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // Do NOT verify peer
7481
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // Do NOT verify host

src/include/cpp-client/api/paths.h

+2
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ extern std::pair<std::string, std::string> search(Host& newHost,
9494
const std::map<std::string, std::string>& bodyParameters,
9595
int limit = 5, int page = 1);
9696
/***/
97+
extern std::pair<std::string, std::string> send(Host& newHost, std::string& jsonTransaction);
98+
/***/
9799
}; // namespace Transactions
98100

99101
/***/

src/include/cpp-client/api/transactions/transactions.h

+3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ class ITransactions : public API::Base {
3434
virtual std::string types() = 0;
3535
virtual std::string search(const std::map<std::string, std::string>& body_parameters, int limit = 5,
3636
int page = 1) = 0;
37+
virtual std::string send(std::string& jsonTransaction) = 0;
38+
3739
};
3840
/**/
3941
class Transactions : public ITransactions {
@@ -46,6 +48,7 @@ class Transactions : public ITransactions {
4648
std::string allUnconfirmed(int limit = 2, int page = 1) override;
4749
std::string types() override;
4850
std::string search(const std::map<std::string, std::string>& body_parameters, int limit = 5, int page = 1) override;
51+
std::string send(std::string& jsonTransaction) override;
4952
};
5053
/**/
5154
}; // namespace API

test/api/paths.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ TEST(paths, test_transactions) { // NOLINT
106106
const auto search = Ark::Client::API::Paths::Transactions::search(testHost, searchBody, 5, 1);
107107
ASSERT_STREQ("0.0.0.0:4003/api/v2/transactions/search?limit=5&page=1", search.first.c_str());
108108
ASSERT_STREQ("id=dummy", search.second.c_str());
109+
110+
std::string jsonTransaction = "{\"id\":\"5ab523d18ac948da82700a71fc0b3c9e764fc0cba91927cb1aa63354564ad23f\",\"signature\":\"3045022100a6da60f9b3e20c80f491d168b8c51a85e0ec56a2448f9e10fc4bcc05a2bf79b8022078fa21b7d46e14c62d38f07e408fdb52f7b6a671894c6d0762913ca4a55e7a99\",\"timestamp\":4076176416,\"type\":0,\"fee\":10000000,\"senderPublicKey\":\"02f21aca9b6d224ea86a1689f57910534af21c3cc9f80602fed252c13e275f0699\",\"amount\":1,\"recipientId\":\"DHQ4Fjsyiop3qBR4otAjAu6cBHkgRELqGA\",\"vendorField\":\"7ad0eeb302ee7d9b4e58cf52daa9ece7922ad92d14f0407e3881597bf3c9c1c6\"}";
111+
const auto send = Ark::Client::API::Paths::Transactions::send(testHost, jsonTransaction);
112+
ASSERT_STREQ("0.0.0.0:4003/api/v2/transactions", send.first.c_str());
113+
ASSERT_STREQ(jsonTransaction.c_str(), send.second.c_str());
109114
}
110115

111116
/***/

test/api/transactions.cpp

+64
Original file line numberDiff line numberDiff line change
@@ -575,3 +575,67 @@ TEST(api, test_transactions_search) { // NOLINT
575575
const char* signature = data["signature"];
576576
ASSERT_STREQ("dummy", signature);
577577
}
578+
579+
/* test_transactions_transactions_send
580+
*
581+
* Expected Response:
582+
* {
583+
* "data": {
584+
* "accept": [
585+
* "dummy"
586+
* ],
587+
* "broadcast": [
588+
* "dummy"
589+
* ],
590+
* "excess": [],
591+
* "invalid": []
592+
* },
593+
* "errors": null
594+
* }
595+
*/
596+
TEST(api, test_transactions_send) { // NOLINT
597+
Ark::Client::Connection<MockApi> connection("167.114.29.55", 4003);
598+
599+
auto apiVersion = connection.api.version();
600+
ASSERT_EQ(2, apiVersion);
601+
602+
const std::string response = R"({
603+
"data": {
604+
"accept": [
605+
"dummy"
606+
],
607+
"broadcast": [
608+
"dummy"
609+
],
610+
"excess": [],
611+
"invalid": []
612+
},
613+
"errors": null
614+
})";
615+
616+
EXPECT_CALL(connection.api.transactions, send(_)).Times(1).WillOnce(Return(response));
617+
618+
std::string jsonTransaction = "{\"transactions\":[{\"type\":0,\"amount\":1,\"fee\":10000000,\"id\":\"bc5bb5cd23521c041fca17b5f78d6f3621fc07ab8f6581aff1b6eb86fa4bafe2\",\"recipientId\":\"DNSrsDUq5injGBdNXPV7v7u1Qy9LZfWEdM\",\"senderPublicKey\":\"0216fa03d378b6ad01325e186ad2cbb9d18976d5b27d0ca74b4f92bb6bf9a6d4d9\",\"signature\":\"3044022014204515b82cdd47513377d3e80e6b5f4fd1ab0fb6b4c181e09a7a30428d542502205ba076a332997053e1d31b506777a99f93bcb11294cd678ebe2da313eb02cae2\",\"timestamp\":58351951,\"vendorField\":\"7ad0eeb302ee7d9b4e58cf52daa9ece7922ad92d14f0407e3881597bf3c9c1c6\"}]}";
619+
620+
const auto transaction = connection.api.transactions.send(jsonTransaction);
621+
622+
DynamicJsonBuffer jsonBuffer(transaction.size());
623+
JsonObject& root = jsonBuffer.parseObject(transaction);
624+
625+
JsonObject& data = root["data"];
626+
627+
std::string accept = data["accept"];
628+
ASSERT_TRUE(accept.length() != 0);
629+
630+
std::string broadcast = data["broadcast"];
631+
ASSERT_TRUE(broadcast.length() != 0);
632+
633+
std::string excess = data["excess"];
634+
ASSERT_TRUE(excess.length() == 2);
635+
636+
std::string invalid = data["invalid"];
637+
ASSERT_TRUE(invalid.length() == 2);
638+
639+
std::string errors = data["errors"];
640+
ASSERT_TRUE(errors.length() == 0 );
641+
}

test/http/http.cpp

+23-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ TEST(api, test_http_get) { // NOLINT
2020
ASSERT_TRUE(obj.containsKey("data"));
2121
}
2222

23-
TEST(api, test_http_post) { // NOLINT
23+
// Tests POSTing of HTTP body.
24+
TEST(api, test_http_post_body) { // NOLINT
2425
// Create the HTTP object
2526
const auto http = Ark::Client::makeHTTP();
2627

@@ -40,6 +41,27 @@ TEST(api, test_http_post) { // NOLINT
4041
ASSERT_TRUE(obj.containsKey("meta"));
4142
}
4243

44+
// Tests POSTing of JSON.
45+
TEST(api, test_http_post_json) { // NOLINT
46+
// Create the HTTP object
47+
const auto http = Ark::Client::makeHTTP();
48+
49+
// Create a Request URL and an empty Transaction JSON.
50+
const auto request = "167.114.29.55:4003/api/v2/transactions";
51+
const auto txJson = "{\"transactions\":[]}";
52+
53+
// Post the 'request' and 'txJson' for a response using HTTP
54+
const auto response = http->post(request, txJson);
55+
56+
// Create a JSON object of the result
57+
DynamicJsonBuffer jsonBuffer(response.length());
58+
JsonObject& obj = jsonBuffer.parseObject(response.c_str());
59+
60+
// Test JSON object for the "message" key.
61+
// The correct response will include the following
62+
ASSERT_STREQ("child \"transactions\" fails because [\"transactions\" must contain at least 1 items]", obj["message"]);
63+
}
64+
4365
// This tests the use of "http://" in single-line HTTP requests.
4466
TEST(api, test_http_request_strings) { // NOLINT
4567

test/mocks/mock_api.h

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ class MockTransactions : public Ark::Client::API::ITransactions {
6262
MOCK_METHOD2(allUnconfirmed, std::string(int, int));
6363
MOCK_METHOD0(types, std::string());
6464
MOCK_METHOD3(search, std::string(const std::map<std::string, std::string>&, int, int));
65+
MOCK_METHOD1(send, std::string(std::string&));
6566
};
6667

6768
class MockVotes : public Ark::Client::API::IVotes {

0 commit comments

Comments
 (0)