Merge pull request #6857 from Icinga/bugfix/check_nscp_api-query-sorted-6536

Url#m_Query: preserve order
This commit is contained in:
Michael Friedrich 2019-02-11 17:57:32 +01:00 committed by GitHub
commit b08d485a41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 67 additions and 104 deletions

View File

@ -42,11 +42,11 @@ void ApiClient::ExecuteScript(const String& session, const String& command, bool
url->SetPort(m_Connection->GetPort());
url->SetPath({ "v1", "console", "execute-script" });
std::map<String, std::vector<String> > params;
params["session"].push_back(session);
params["command"].push_back(command);
params["sandboxed"].emplace_back(sandboxed ? "1" : "0");
url->SetQuery(params);
url->SetQuery({
{"session", session},
{"command", command},
{"sandboxed", sandboxed ? "1" : "0"}
});
try {
std::shared_ptr<HttpRequest> req = m_Connection->NewRequest();
@ -121,11 +121,11 @@ void ApiClient::AutocompleteScript(const String& session, const String& command,
url->SetPort(m_Connection->GetPort());
url->SetPath({ "v1", "console", "auto-complete-script" });
std::map<String, std::vector<String> > params;
params["session"].push_back(session);
params["command"].push_back(command);
params["sandboxed"].emplace_back(sandboxed ? "1" : "0");
url->SetQuery(params);
url->SetQuery({
{"session", session},
{"command", command},
{"sandboxed", sandboxed ? "1" : "0"}
});
try {
std::shared_ptr<HttpRequest> req = m_Connection->NewRequest();

View File

@ -20,6 +20,8 @@
#include "remote/httputility.hpp"
#include "base/json.hpp"
#include "base/logger.hpp"
#include <map>
#include <vector>
using namespace icinga;
@ -44,8 +46,12 @@ Dictionary::Ptr HttpUtility::FetchRequestParameters(HttpRequest& request)
if (!result)
result = new Dictionary();
typedef std::pair<String, std::vector<String> > kv_pair;
for (const kv_pair& kv : request.RequestUrl->GetQuery()) {
std::map<String, std::vector<String>> query;
for (const auto& kv : request.RequestUrl->GetQuery()) {
query[kv.first].emplace_back(kv.second);
}
for (auto& kv : query) {
result->Set(kv.first, Array::FromVector(kv.second));
}

View File

@ -138,33 +138,11 @@ const std::vector<String>& Url::GetPath() const
return m_Path;
}
const std::map<String, std::vector<String> >& Url::GetQuery() const
const std::vector<std::pair<String, String>>& Url::GetQuery() const
{
return m_Query;
}
String Url::GetQueryElement(const String& name) const
{
auto it = m_Query.find(name);
if (it == m_Query.end())
return String();
return it->second.back();
}
const std::vector<String>& Url::GetQueryElements(const String& name) const
{
auto it = m_Query.find(name);
if (it == m_Query.end()) {
static std::vector<String> emptyVector;
return emptyVector;
}
return it->second;
}
String Url::GetFragment() const
{
return m_Fragment;
@ -200,7 +178,7 @@ void Url::SetPath(const std::vector<String>& path)
m_Path = path;
}
void Url::SetQuery(const std::map<String, std::vector<String> >& query)
void Url::SetQuery(const std::vector<std::pair<String, String>>& query)
{
m_Query = query;
}
@ -212,16 +190,7 @@ void Url::SetArrayFormatUseBrackets(bool useBrackets)
void Url::AddQueryElement(const String& name, const String& value)
{
auto it = m_Query.find(name);
if (it == m_Query.end()) {
m_Query[name] = std::vector<String> { value };
} else
m_Query[name].push_back(value);
}
void Url::SetQueryElements(const String& name, const std::vector<String>& values)
{
m_Query[name] = values;
m_Query.emplace_back(name, value);
}
void Url::SetFragment(const String& fragment) {
@ -255,38 +224,16 @@ String Url::Format(bool onlyPathAndQuery, bool printCredentials) const
if (!m_Query.empty()) {
typedef std::pair<String, std::vector<String> > kv_pair;
for (const kv_pair& kv : m_Query) {
for (const auto& kv : m_Query) {
String key = Utility::EscapeString(kv.first, ACQUERY_ENCODE, false);
if (param.IsEmpty())
param = "?";
else
param += "&";
// Just one (or one empty) value
if (kv.second.size() == 1) {
param += key;
param += kv.second[0].IsEmpty() ?
String() : "=" + Utility::EscapeString(kv.second[0], ACQUERY_ENCODE, false);
continue;
}
// Array
String temp;
for (const String& s : kv.second) {
if (!temp.IsEmpty())
temp += "&";
temp += key;
if (m_ArrayFormatUseBrackets) {
if (kv.second.size() > 1)
temp += "[]";
}
if (!s.IsEmpty())
temp += "=" + Utility::EscapeString(s, ACQUERY_ENCODE, false);
}
param += temp;
param += key;
param += kv.second.IsEmpty() ?
String() : "=" + Utility::EscapeString(kv.second, ACQUERY_ENCODE, false);
}
}
@ -408,14 +355,7 @@ bool Url::ParseQuery(const String& query)
if (!ValidateToken(key, ACQUERY))
return false;
key = Utility::UnescapeString(key);
auto it = m_Query.find(key);
if (it == m_Query.end()) {
m_Query[key] = std::vector<String> { std::move(value) };
} else
m_Query[key].emplace_back(std::move(value));
m_Query.emplace_back(Utility::UnescapeString(key), std::move(value));
}
return true;

View File

@ -26,6 +26,7 @@
#include "base/array.hpp"
#include "base/value.hpp"
#include <map>
#include <utility>
#include <vector>
namespace icinga
@ -53,9 +54,7 @@ public:
String GetHost() const;
String GetPort() const;
const std::vector<String>& GetPath() const;
const std::map<String, std::vector<String> >& GetQuery() const;
String GetQueryElement(const String& name) const;
const std::vector<String>& GetQueryElements(const String& name) const;
const std::vector<std::pair<String, String>>& GetQuery() const;
String GetFragment() const;
void SetScheme(const String& scheme);
@ -64,11 +63,10 @@ public:
void SetHost(const String& host);
void SetPort(const String& port);
void SetPath(const std::vector<String>& path);
void SetQuery(const std::map<String, std::vector<String> >& query);
void SetQuery(const std::vector<std::pair<String, String>>& query);
void SetArrayFormatUseBrackets(bool useBrackets = true);
void AddQueryElement(const String& name, const String& query);
void SetQueryElements(const String& name, const std::vector<String>& query);
void SetFragment(const String& fragment);
private:
@ -78,7 +76,7 @@ private:
String m_Host;
String m_Port;
std::vector<String> m_Path;
std::map<String, std::vector<String> > m_Query;
std::vector<std::pair<String, String>> m_Query;
bool m_ArrayFormatUseBrackets;
String m_Fragment;

View File

@ -53,31 +53,50 @@ BOOST_AUTO_TEST_CASE(get_and_set)
BOOST_CHECK(url->Format(false, true) == "ftp://Horst:Seehofer@koenigreich.bayern:1918/path/to/m%C3%BCnchen");
std::map<String, std::vector<String> > m;
std::vector<String> v1 { "hip", "hip", "hurra" };
std::vector<String> v2 { "äü^ä+#ül-" };
std::vector<String> v3 { "1", "2" };
m.insert(std::make_pair("shout", v1));
m.insert(std::make_pair("sonderzeichen", v2));
url->SetQuery(m);
url->SetQueryElements("count", v3);
url->SetQuery({
{"shout", "hip"},
{"shout", "hip"},
{"shout", "hurra"},
{"sonderzeichen", "äü^ä+#ül-"}
});
url->AddQueryElement("count", "3");
std::map<String, std::vector<String> > mn = url->GetQuery();
BOOST_CHECK(mn["shout"][0] == v1[0]);
BOOST_CHECK(mn["sonderzeichen"][0] == v2[0]);
BOOST_CHECK(mn["count"][2] == "3");
auto mn (url->GetQuery());
BOOST_CHECK(mn.size() == 5);
BOOST_CHECK(mn[0].first == "shout");
BOOST_CHECK(mn[0].second == "hip");
BOOST_CHECK(mn[1].first == "shout");
BOOST_CHECK(mn[1].second == "hip");
BOOST_CHECK(mn[2].first == "shout");
BOOST_CHECK(mn[2].second == "hurra");
BOOST_CHECK(mn[3].first == "sonderzeichen");
BOOST_CHECK(mn[3].second == "äü^ä+#ül-");
BOOST_CHECK(mn[4].first == "count");
BOOST_CHECK(mn[4].second == "3");
}
BOOST_AUTO_TEST_CASE(parameters)
{
Url::Ptr url = new Url("https://icinga.com/hya/?rain=karl&rair=robert&foo[]=bar");
Url::Ptr url = new Url("https://icinga.com/hya/?rair=robert&rain=karl&foo[]=bar");
BOOST_CHECK(url->GetQueryElement("rair") == "robert");
BOOST_CHECK(url->GetQueryElement("rain") == "karl");
std::vector<String> test = url->GetQueryElements("foo");
BOOST_CHECK(test.size() == 1);
BOOST_CHECK(test[0] == "bar");
auto query (url->GetQuery());
BOOST_CHECK(query.size() == 3);
BOOST_CHECK(query[0].first == "rair");
BOOST_CHECK(query[0].second == "robert");
BOOST_CHECK(query[1].first == "rain");
BOOST_CHECK(query[1].second == "karl");
BOOST_CHECK(query[2].first == "foo");
BOOST_CHECK(query[2].second == "bar");
}
BOOST_AUTO_TEST_CASE(format)