Skip to content

Commit ef4ec55

Browse files
committed
Enhance rpc addpoll to properly handle (required) additional fields
This also makes some modifications to PollBuilder.
1 parent ed6ad37 commit ef4ec55

File tree

3 files changed

+157
-16
lines changed

3 files changed

+157
-16
lines changed

src/gridcoin/voting/builders.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,13 @@ PollBuilder PollBuilder::SetAdditionalFields(std::vector<Poll::AdditionalField>
11021102
return AddAdditionalFields(std::move(fields));
11031103
}
11041104

1105+
PollBuilder PollBuilder::SetAdditionalFields(Poll::AdditionalFieldList fields)
1106+
{
1107+
m_poll->m_additional_fields = Poll::AdditionalFieldList();
1108+
1109+
return AddAdditionalFields(std::move(fields));
1110+
}
1111+
11051112
PollBuilder PollBuilder::AddAdditionalFields(std::vector<Poll::AdditionalField> fields)
11061113
{
11071114
for (auto& field : fields) {
@@ -1111,11 +1118,21 @@ PollBuilder PollBuilder::AddAdditionalFields(std::vector<Poll::AdditionalField>
11111118
return std::move(*this);
11121119
}
11131120

1121+
PollBuilder PollBuilder::AddAdditionalFields(Poll::AdditionalFieldList fields)
1122+
{
1123+
for (auto& field : fields) {
1124+
*this = AddAdditionalField(std::move(field));
1125+
}
1126+
1127+
return std::move(*this);
1128+
}
1129+
11141130
PollBuilder PollBuilder::AddAdditionalField(Poll::AdditionalField field)
11151131
{
11161132
// Make sure there are no leading and trailing spaces.
11171133
field.m_name = TrimString(field.m_name);
11181134
field.m_value = TrimString(field.m_value);
1135+
field.m_required = field.m_required;
11191136

11201137
if (!field.WellFormed()) {
11211138
throw VotingError(_("The field is not well-formed."));

src/gridcoin/voting/builders.h

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,27 +169,47 @@ class PollBuilder
169169
//!
170170
//! \brief Set the set of additional fields for the poll.
171171
//!
172-
//! \param labels A set of AdditionalFields to set.
172+
//! \param field A set of AdditionalFields to set.
173173
//!
174174
//! \throws VotingError If any of the fields are malformed, or if the set of fields
175175
//! contains a duplicate label.
176176
//!
177177
PollBuilder SetAdditionalFields(std::vector<Poll::AdditionalField> fields);
178178

179+
//!
180+
//! \brief Set the set of additional fields for the poll.
181+
//!
182+
//! \param fields A set of AdditionalFields to set.
183+
//!
184+
//! \throws VotingError If any of the fields are malformed, or if the set of fields
185+
//! contains a duplicate label.
186+
//!
187+
PollBuilder SetAdditionalFields(Poll::AdditionalFieldList fields);
188+
179189
//!
180190
//! \brief Add a set of additional fields for the poll.
181191
//!
182-
//! \param labels A set of AdditionalFields to add.
192+
//! \param fields A set of AdditionalFields to add.
183193
//!
184194
//! \throws VotingError If any of the fields are malformed, or if the set of fields
185195
//! contains a duplicate label.
186196
//!
187197
PollBuilder AddAdditionalFields(std::vector<Poll::AdditionalField> fields);
188198

199+
//!
200+
//! \brief Add a set of additional fields for the poll.
201+
//!
202+
//! \param fields A set of AdditionalFields to add.
203+
//!
204+
//! \throws VotingError If any of the fields are malformed, or if the set of fields
205+
//! contains a duplicate label.
206+
//!
207+
PollBuilder AddAdditionalFields(Poll::AdditionalFieldList fields);
208+
189209
//!
190210
//! \brief Add an additional field for the poll.
191211
//!
192-
//! \param AdditionalField The additional field name-value to add.
212+
//! \param field The additional field name-value to add.
193213
//!
194214
//! \throws VotingError If the field is malformed, or if the set of fields
195215
//! contains a duplicate label.

src/rpc/voting.cpp

Lines changed: 117 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,25 @@ UniValue PollChoicesToJson(const Poll::ChoiceList& choices)
5151
return json;
5252
}
5353

54+
UniValue PollAdditionalFieldsToJson(const Poll::AdditionalFieldList fields)
55+
{
56+
UniValue json(UniValue::VARR);
57+
58+
for (size_t i = 0; i < fields.size(); ++i) {
59+
UniValue field(UniValue::VOBJ);
60+
UniValue field_value(UniValue::VOBJ);
61+
62+
field_value.pushKV("value", fields.At(i)->m_value);
63+
field_value.pushKV("required", fields.At(i)->m_required);
64+
65+
field.pushKV(fields.At(i)->m_name, field_value);
66+
67+
json.push_back(field);
68+
}
69+
70+
return json;
71+
}
72+
5473
UniValue PollToJson(const Poll& poll, const uint256 txid)
5574
{
5675
UniValue json(UniValue::VOBJ);
@@ -59,6 +78,7 @@ UniValue PollToJson(const Poll& poll, const uint256 txid)
5978
json.pushKV("id", txid.ToString());
6079
json.pushKV("question", poll.m_question);
6180
json.pushKV("url", poll.m_url);
81+
json.pushKV("additional_fields", PollAdditionalFieldsToJson(poll.AdditionalFields()));
6282
json.pushKV("poll_type", poll.PollTypeToString());
6383
json.pushKV("poll_type_id", (int)poll.m_type.Raw());
6484
json.pushKV("weight_type", poll.WeightTypeToString());
@@ -318,29 +338,31 @@ UniValue addpoll(const UniValue& params, bool fHelp)
318338
types_ss << ToLower(Poll::PollTypeToString(type, false));
319339
}
320340

321-
if (fHelp || params.size() != 8) {
341+
if (params.size() == 0) {
322342
std::string e = strprintf(
323-
"addpoll <type> <title> <days> <question> <answer1;answer2...> <weighttype> <responsetype> <url>\n"
343+
"addpoll <type> <title> <days> <question> <answer1;answer2...> <weighttype> <responsetype> <url> "
344+
"<required_field_name1=value1;required_field_name2=value2...>\n"
324345
"\n"
325-
"<type> ---------> Type of poll. Valid types are: %s.\n"
326-
"<title> --------> Title for the poll\n"
327-
"<days> ---------> Number of days that the poll will run\n"
328-
"<question> -----> Prompt that voters shall answer\n"
329-
"<answers> ------> Answers for voters to choose from. Separate answers with semicolons (;)\n"
330-
"<weighttype> ---> Weighing method for the poll: 1 = Balance, 2 = Magnitude + Balance\n"
331-
"<responsetype> -> 1 = yes/no/abstain, 2 = single-choice, 3 = multiple-choice\n"
332-
"<url> ----------> Discussion web page URL for the poll\n"
346+
"<type> -----------> Type of poll. Valid types are: %s.\n"
347+
"<title> ----------> Title for the poll\n"
348+
"<days> -----------> Number of days that the poll will run\n"
349+
"<question> -------> Prompt that voters shall answer\n"
350+
"<answers> --------> Answers for voters to choose from. Separate answers with semicolons (;)\n"
351+
"<weighttype> -----> Weighing method for the poll: 1 = Balance, 2 = Magnitude + Balance\n"
352+
"<responsetype> ---> 1 = yes/no/abstain, 2 = single-choice, 3 = multiple-choice\n"
353+
"<url> ------------> Discussion web page URL for the poll\n"
354+
"<required fields>-> Required additional field(s) if any (see below)\n"
333355
"\n"
334356
"Add a poll to the network.\n"
335357
"Requires 100K GRC balance. Costs 50 GRC.\n"
336-
"Provide an empty string for <answers> when choosing \"yes/no/abstain\" for <responsetype>.\n",
358+
"Provide an empty string for <answers> when choosing \"yes/no/abstain\" for <responsetype>.\n"
359+
"Certain poll types may require additional fields. You can see these with addpoll <type> \n"
360+
"with no other parameters.",
337361
types_ss.str());
338362

339363
throw std::runtime_error(e);
340364
}
341365

342-
EnsureWalletIsUnlocked();
343-
344366
std::string type_string = ToLower(params[0].get_str());
345367

346368
PollType poll_type;
@@ -361,6 +383,55 @@ UniValue addpoll(const UniValue& params, bool fHelp)
361383
throw JSONRPCError(RPC_INVALID_PARAMETER, e);
362384
}
363385

386+
const std::vector<std::string>& required_fields = Poll::POLL_TYPE_RULES[(int) poll_type].m_required_fields;
387+
std::stringstream required_fields_ss;
388+
389+
for (const auto& required_field : required_fields) {
390+
if (required_fields_ss.str() != std::string{}) {
391+
required_fields_ss << ", ";
392+
}
393+
394+
required_fields_ss << required_field;
395+
}
396+
397+
if (params.size() == 1) {
398+
std::string e = strprintf(
399+
"For addpoll %s, the required fields are the following: %s.\n",
400+
ToLower(params[0].get_str()),
401+
required_fields.empty() ? "none" : required_fields_ss.str());
402+
403+
throw std::runtime_error(e);
404+
}
405+
406+
size_t required_number_of_params = required_fields.empty() ? 8 : 9;
407+
408+
if (fHelp || params.size() < required_number_of_params) {
409+
std::string e = strprintf(
410+
"addpoll <type> <title> <days> <question> <answer1;answer2...> <weighttype> <responsetype> <url> "
411+
"<required_field_name1=value1;required_field_name2=value2...>\n"
412+
"\n"
413+
"<type> -----------> Type of poll. Valid types are: %s.\n"
414+
"<title> ----------> Title for the poll\n"
415+
"<days> -----------> Number of days that the poll will run\n"
416+
"<question> -------> Prompt that voters shall answer\n"
417+
"<answers> --------> Answers for voters to choose from. Separate answers with semicolons (;)\n"
418+
"<weighttype> -----> Weighing method for the poll: 1 = Balance, 2 = Magnitude + Balance\n"
419+
"<responsetype> ---> 1 = yes/no/abstain, 2 = single-choice, 3 = multiple-choice\n"
420+
"<url> ------------> Discussion web page URL for the poll\n"
421+
"<required fields>-> Required additional field(s) if any (see below)\n"
422+
"\n"
423+
"Add a poll to the network.\n"
424+
"Requires 100K GRC balance. Costs 50 GRC.\n"
425+
"Provide an empty string for <answers> when choosing \"yes/no/abstain\" for <responsetype>.\n"
426+
"Certain poll types may require additional fields. You can see these with addpoll <type> \n"
427+
"with no other parameters.",
428+
types_ss.str());
429+
430+
throw std::runtime_error(e);
431+
}
432+
433+
EnsureWalletIsUnlocked();
434+
364435
PollBuilder builder = PollBuilder()
365436
.SetPayloadVersion(payload_version)
366437
.SetType(poll_type)
@@ -375,6 +446,39 @@ UniValue addpoll(const UniValue& params, bool fHelp)
375446
builder = builder.SetChoices(split(params[4].get_str(), ";"));
376447
}
377448

449+
if (params.size() == 9 && !params[8].isNull() && !params[8].get_str().empty()) {
450+
std::vector<std::string> name_value_pairs = split(params[8].get_str(), ";");
451+
Poll::AdditionalFieldList fields;
452+
453+
for (const auto& name_value_pair : name_value_pairs) {
454+
std::vector v_field = split(name_value_pair, "=");
455+
bool required = true;
456+
457+
if (v_field.size() != 2) {
458+
throw std::runtime_error("Required fields parameter for poll is malformed.");
459+
}
460+
461+
std::string field_name = TrimString(v_field[0]);
462+
std::string field_value = TrimString(v_field[1]);
463+
464+
if (std::find(required_fields.begin(), required_fields.end(), field_name) == required_fields.end()) {
465+
required = false;
466+
}
467+
468+
Poll::AdditionalField field(field_name, field_value, required);
469+
470+
fields.Add(field);
471+
}
472+
473+
// TODO: Extend Wellformed to do a duplicate check on the field name? This is done in the builder anyway. This
474+
// makes sure that at least the required fields have been provided and that they are well formed.
475+
if (!fields.WellFormed(poll_type)) {
476+
throw std::runtime_error("Required field list is malformed.");
477+
}
478+
479+
builder = builder.AddAdditionalFields(fields);
480+
}
481+
378482
std::pair<CWalletTx, std::string> result_pair;
379483

380484
{

0 commit comments

Comments
 (0)