Merge branch 'develop' into main

This commit is contained in:
tastytea 2020-01-11 16:46:20 +01:00
commit 23eb3ff43e
Signed by: tastytea
GPG Key ID: CFC39497F1B26E07
13 changed files with 581 additions and 72 deletions

View File

@ -53,12 +53,12 @@ Have a look at the link:{uri-reference}[reference].
int main()
{
mastodonpp::Instance instance{"example.com", {}};
mastodonpp::Instance instance{"example.com", "123AccessToken123"};
mastodonpp::Connection connection{instance};
const mastodonpp::parametermap parameters
{
{"status", "How is the wheather?"},
{"status", "How is the weather?"},
{"poll[options]", vector<string_view>{"Nice", "not nice"}},
{"poll[expires_in]", "86400"}
};
@ -88,7 +88,7 @@ emerge -a dev-cpp/mastodonpp
=== Debian and Ubuntu
We automatically generate packages for Debian buster (10) and Ubuntu bionic
(18.04), but only for x86_64 (amd64). Download the them at
(18.04), but only for x86_64 (amd64). Download them at
link:{uri-base}/releases[schlomp.space].
[source,shell]
@ -99,7 +99,7 @@ apt install ./libmastodonpp*.deb
=== CentOS
We automatically generate packages for CentOS 8, but only for x86_64
(amd64). Download the them at link:{uri-base}/releases[schlomp.space].
(amd64). Download them at link:{uri-base}/releases[schlomp.space].
[source,shell]
--------------------------------------------------------------------------------

View File

@ -33,7 +33,7 @@ using std::vector;
int main(int argc, char *argv[])
{
const vector<string_view> args(argv, argv + argc);
if (args.size() <= 1)
if (args.size() <= 2)
{
cerr << "Usage: " << args[0] << " <instance hostname> <access token>\n";
return 1;
@ -49,7 +49,7 @@ int main(int argc, char *argv[])
constexpr auto poll_seconds{60 * 60 * 24 * 2}; // 2 days.
const masto::parametermap parameters
{
{"status", "How is the wheather?"},
{"status", "How is the weather?"},
{"poll[options]", vector<string_view>{"Nice", "not nice"}},
{"poll[expires_in]", to_string(poll_seconds)}
};

View File

@ -0,0 +1,100 @@
/* This file is part of mastodonpp.
* Copyright © 2020 tastytea <tastytea@tastytea.de>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
// Post a status (/api/v1/status) with an attachment (/api/v1/media).
#include "mastodonpp.hpp"
#include <iostream>
#include <string>
#include <string_view>
#include <vector>
namespace masto = mastodonpp;
using std::cout;
using std::cerr;
using std::endl;
using std::string;
using std::to_string;
using std::string_view;
using std::vector;
int main(int argc, char *argv[])
{
const vector<string_view> args(argv, argv + argc);
if (args.size() <= 3)
{
cerr << "Usage: " << args[0]
<< " <instance hostname> <access token> <file>\n";
return 1;
}
try
{
// Initialize an Instance and a Connection.
masto::Instance instance{args[1], args[2]};
masto::Connection connection{instance};
const string_view filename{args[3]};
// Create attachment.
auto answer{connection.post(masto::API::v1::media,
{
{"file", string("@file:") += filename},
{"description", "Test."}
})};
// Get the ID of the attachment.
// You normally would use a JSON parser, of course. I don't use one
// because I don't want to add a dependency just for an example.
const auto pos{answer.body.find(R"("id":")") + 6};
const auto endpos{answer.body.find(R"(",)", pos)};
const auto media_id{answer.body.substr(pos, endpos - pos)};
cout << "Attachment has ID: " << media_id << endl;
// Post the status. Note that “media_ids” always has to be a vector.
answer = connection.post(masto::API::v1::statuses,
{
{"status", "Attachment test."},
{"media_ids",
vector<string_view>{media_id}}
});
if (answer)
{
cout << "Successfully posted " << filename << ".\n";
}
else
{
if (answer.curl_error_code == 0)
{
// If it is no libcurl error, it must be an HTTP error.
cerr << "HTTP status: " << answer.http_status << endl;
}
else
{
// Network errors like “Couldn't resolve host.”.
cerr << "libcurl error " << to_string(answer.curl_error_code)
<< ": " << answer.error_message << endl;
}
}
}
catch (const masto::CURLException &e)
{
// Only libcurl errors that are not network errors will be thrown.
// There went probably something wrong with the initialization.
cerr << e.what() << endl;
}
return 0;
}

View File

@ -0,0 +1,84 @@
/* This file is part of mastodonpp.
* Copyright © 2020 tastytea <tastytea@tastytea.de>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
// Update notification settings (/api/pleroma/notification_settings).
#include "mastodonpp.hpp"
#include <iostream>
#include <string>
#include <string_view>
#include <vector>
namespace masto = mastodonpp;
using std::cout;
using std::cerr;
using std::endl;
using std::to_string;
using std::string_view;
using std::vector;
int main(int argc, char *argv[])
{
const vector<string_view> args(argv, argv + argc);
if (args.size() <= 2)
{
cerr << "Usage: " << args[0] << " <instance hostname> <access token>\n";
return 1;
}
try
{
// Initialize an Instance and a Connection.
masto::Instance instance{args[1], args[2]};
masto::Connection connection{instance};
// Update the settings.
const auto answer{connection.put(
masto::API::pleroma::notification_settings,
{
{"followers", "true"},
{"follows", "true"},
{"remote", "true"},
{"local", "true"},
})};
if (answer)
{
cout << answer << endl;
}
else
{
if (answer.curl_error_code == 0)
{
// If it is no libcurl error, it must be an HTTP error.
cerr << "HTTP status: " << answer.http_status << endl;
}
else
{
// Network errors like “Couldn't resolve host.”.
cerr << "libcurl error " << to_string(answer.curl_error_code)
<< ": " << answer.error_message << endl;
}
}
}
catch (const masto::CURLException &e)
{
// Only libcurl errors that are not network errors will be thrown.
// There went probably something wrong with the initialization.
cerr << e.what() << endl;
}
return 0;
}

View File

@ -0,0 +1,83 @@
/* This file is part of mastodonpp.
* Copyright © 2020 tastytea <tastytea@tastytea.de>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
// Update account display name settings (/api/v1/accounts/update_credentials).
#include "mastodonpp.hpp"
#include <iostream>
#include <string>
#include <string_view>
#include <vector>
namespace masto = mastodonpp;
using std::cout;
using std::cerr;
using std::endl;
using std::to_string;
using std::string_view;
using std::vector;
int main(int argc, char *argv[])
{
const vector<string_view> args(argv, argv + argc);
if (args.size() <= 3)
{
cerr << "Usage: " << args[0]
<< " <instance hostname> <access token> <name>\n";
return 1;
}
const auto name{args[3]};
try
{
// Initialize an Instance and a Connection.
masto::Instance instance{args[1], args[2]};
masto::Connection connection{instance};
// Update the settings.
const auto answer{connection.patch(
masto::API::v1::accounts_update_credentials,
{
{"display_name", name},
})};
if (answer)
{
cout << "Successfully changed display name.\n";
}
else
{
if (answer.curl_error_code == 0)
{
// If it is no libcurl error, it must be an HTTP error.
cerr << "HTTP status: " << answer.http_status << endl;
}
else
{
// Network errors like “Couldn't resolve host.”.
cerr << "libcurl error " << to_string(answer.curl_error_code)
<< ": " << answer.error_message << endl;
}
}
}
catch (const masto::CURLException &e)
{
// Only libcurl errors that are not network errors will be thrown.
// There went probably something wrong with the initialization.
cerr << e.what() << endl;
}
return 0;
}

View File

@ -0,0 +1,98 @@
/* This file is part of mastodonpp.
* Copyright © 2020 tastytea <tastytea@tastytea.de>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
// Post a status (/api/v1/status), then delete it (/api/v1/statuses/:id).
#include "mastodonpp.hpp"
#include <chrono>
#include <iostream>
#include <string>
#include <string_view>
#include <thread>
#include <vector>
namespace masto = mastodonpp;
using namespace std::chrono_literals;
using std::cout;
using std::cerr;
using std::endl;
using std::to_string;
using std::string_view;
using std::this_thread::sleep_for;
using std::vector;
int main(int argc, char *argv[])
{
const vector<string_view> args(argv, argv + argc);
if (args.size() <= 2)
{
cerr << "Usage: " << args[0] << " <instance hostname> <access token>\n";
return 1;
}
try
{
// Initialize an Instance and a Connection.
masto::Instance instance{args[1], args[2]};
masto::Connection connection{instance};
// Post a status.
auto answer{connection.post(masto::API::v1::statuses,
{{"status", "Delete me."}})};
if (answer)
{
cout << "Successfully posted a status.\n";
// Get the ID of the post.
// You normally would use a JSON parser, of course. I don't use one
// because I don't want to add a dependency just for an example.
const auto pos{answer.body.rfind(R"("id":")") + 6};
const auto endpos{answer.body.find(R"(",)", pos)};
const auto id{answer.body.substr(pos, endpos - pos)};
cout << "Post has ID: " << id << endl;
cout << "Waiting 10 seconds…\n";
sleep_for(10s);
answer = connection.del(masto::API::v1::statuses_id, {{"id", id}});
if (answer)
{
cout << "Successfully deleted the status.\n";
}
}
else
{
if (answer.curl_error_code == 0)
{
// If it is no libcurl error, it must be an HTTP error.
cerr << "HTTP status: " << answer.http_status << endl;
}
else
{
// Network errors like “Couldn't resolve host.”.
cerr << "libcurl error " << to_string(answer.curl_error_code)
<< ": " << answer.error_message << endl;
}
}
}
catch (const masto::CURLException &e)
{
// Only libcurl errors that are not network errors will be thrown.
// There went probably something wrong with the initialization.
cerr << e.what() << endl;
}
return 0;
}

View File

@ -281,9 +281,9 @@ public:
disable_account,
account_register,
pleroma_notification_settings,
pleroma_healthcheck,
pleroma_change_email
notification_settings,
healthcheck,
change_email
};
/*!

View File

@ -161,6 +161,84 @@ public:
return post(endpoint, {});
}
/*!
* @brief Make a HTTP PATCH call with parameters.
*
* @param endpoint Endpoint as API::endpoint_type or `std::string_view`.
* @param parameters A map of parameters.
*
*
* @since 0.2.0
*/
[[nodiscard]]
answer_type patch(const endpoint_variant &endpoint,
const parametermap &parameters);
/*!
* @brief Make a HTTP PATCH call.
*
* @param endpoint Endpoint as API::endpoint_type or `std::string_view`.
*
* @since 0.2.0
*/
[[nodiscard]]
inline answer_type patch(const endpoint_variant &endpoint)
{
return patch(endpoint, {});
}
/*!
* @brief Make a HTTP PUT call with parameters.
*
* @param endpoint Endpoint as API::endpoint_type or `std::string_view`.
* @param parameters A map of parameters.
*
*
* @since 0.2.0
*/
[[nodiscard]]
answer_type put(const endpoint_variant &endpoint,
const parametermap &parameters);
/*!
* @brief Make a HTTP PUT call.
*
* @param endpoint Endpoint as API::endpoint_type or `std::string_view`.
*
* @since 0.2.0
*/
[[nodiscard]]
inline answer_type put(const endpoint_variant &endpoint)
{
return put(endpoint, {});
}
/*!
* @brief Make a HTTP DELETE call with parameters.
*
* @param endpoint Endpoint as API::endpoint_type or `std::string_view`.
* @param parameters A map of parameters.
*
*
* @since 0.2.0
*/
[[nodiscard]]
answer_type del(const endpoint_variant &endpoint,
const parametermap &parameters);
/*!
* @brief Make a HTTP DELETE call.
*
* @param endpoint Endpoint as API::endpoint_type or `std::string_view`.
*
* @since 0.2.0
*/
[[nodiscard]]
inline answer_type del(const endpoint_variant &endpoint)
{
return del(endpoint, {});
}
/*!
* @brief Copy new stream contents and delete the original.
*

View File

@ -57,12 +57,17 @@ enum class http_method
/*!
* @brief `std::map` of parameters for %API calls.
*
* Note that arrays always have to be specified as vectors, even if they have
* only 1 element. To send a file, use <tt>\@file:</tt> followed by the file
* name as value.
*
* Example:
* @code
* parametermap parameters
* {
* {"id", "12"},
* {"poll[options]", vector<string_view>{"Yes", "No", "Maybe"}}
* {"poll[expires_in]", "86400"},
* {"poll[options]", vector<string_view>{"Yes", "No", "Maybe"}},
* {"status", "How is the weather?"}
* };
* @endcode
*
@ -294,6 +299,18 @@ private:
*/
void add_parameters_to_uri(string &uri, const parametermap &parameters);
/*!
* @brief Add `*curl_mimepart` to `*curl_mime`.
*
* @param mime Initialized `*curl_mime`. @param name Name of the field.
* @param data Data of the field. If it begins with <tt>`\@file:<tt>, the
* rest of the ergument is treated as a filename.
*
* @since 0.1.1
*/
void add_mime_part(curl_mime *mime,
string_view name, string_view data) const;
/*!
* @brief Convert parametermap to `*curl_mime`.
*

View File

@ -69,7 +69,9 @@
*
* @subsection input Input
*
* All text input is expected to be UTF-8.
* * All text input is expected to be UTF-8.
* * To send a file, use <tt>\@file:</tt> followed by the file name as value
* in the @link mastodonpp::parametermap parametermap@endlink.
*
* @section exceptions Exceptions
*
@ -99,6 +101,10 @@
* @example example01_instance_info.cpp
* @example example02_streaming.cpp
* @example example03_post_status.cpp
* @example example04_post_with_attachment.cpp
* @example example05_update_notification_settings.cpp
* @example example06_update_name.cpp
* @example example07_delete_status.cpp
*/
/*!

View File

@ -31,8 +31,8 @@ const map<API::endpoint_type,string_view> API::_endpoint_map
{v1::apps_verify_credentials, "/api/v1/apps/verify/credentials"},
{v1::accounts, "/api/v1/accounts"},
{v1::accounts_verify_credentials, "/api/v1/accounts/verify/credentials"},
{v1::accounts_update_credentials, "/api/v1/accounts/update/credentials"},
{v1::accounts_verify_credentials, "/api/v1/accounts/verify_credentials"},
{v1::accounts_update_credentials, "/api/v1/accounts/update_credentials"},
{v1::accounts_id, "/api/v1/accounts/<ID>"},
{v1::accounts_id_statuses, "/api/v1/accounts/<ID>/statuses"},
{v1::accounts_id_followers, "/api/v1/accounts/<ID>/followers"},
@ -61,7 +61,7 @@ const map<API::endpoint_type,string_view> API::_endpoint_map
{v1::domain_blocks, "/api/v1/domain/blocks"},
{v1::filters, "/api/v1/filters"},
{v1::filters_id, "/api/v1/filters/id"},
{v1::filters_id, "/api/v1/filters/<ID>"},
{v1::reports, "/api/v1/reports"},
@ -73,16 +73,16 @@ const map<API::endpoint_type,string_view> API::_endpoint_map
{v1::endorsements, "/api/v1/endorsements"},
{v1::featured_tags, "/api/v1/featured/tags"},
{v1::featured_tags_id, "/api/v1/featured/tags/id"},
{v1::featured_tags_id, "/api/v1/featured/tags/<ID>"},
{v1::featured_tags_suggestions, "/api/v1/featured/tags/suggestions"},
{v1::preferences, "/api/v1/preferences"},
{v1::suggestions, "/api/v1/suggestions"},
{v1::suggestions_account_id, "/api/v1/suggestions/account/id"},
{v1::suggestions_account_id, "/api/v1/suggestions/account/<ID>"},
{v1::statuses, "/api/v1/statuses"},
{v1::statuses_id, "/api/v1/statuses/id"},
{v1::statuses_id, "/api/v1/statuses/<ID>"},
{v1::statuses_id_context, "/api/v1/statuses/<ID>/context"},
{v1::statuses_id_reblogged_by, "/api/v1/statuses/<ID>/reblogged/by"},
{v1::statuses_id_favourited_by, "/api/v1/statuses/<ID>/favourited/by"},
@ -98,21 +98,21 @@ const map<API::endpoint_type,string_view> API::_endpoint_map
{v1::statuses_id_unpin, "/api/v1/statuses/<ID>/unpin"},
{v1::media, "/api/v1/media"},
{v1::media_id, "/api/v1/media/id"},
{v1::media_id, "/api/v1/media/<ID>"},
{v1::polls_id, "/api/v1/polls/id"},
{v1::polls_id, "/api/v1/polls/<ID>"},
{v1::polls_id_votes, "/api/v1/polls/<ID>/votes"},
{v1::scheduled_statuses, "/api/v1/scheduled/statuses"},
{v1::scheduled_statuses_id, "/api/v1/scheduled/statuses/id"},
{v1::scheduled_statuses_id, "/api/v1/scheduled/statuses/<ID>"},
{v1::timelines_public, "/api/v1/timelines/public"},
{v1::timelines_tag_hashtag, "/api/v1/timelines/tag/<HASHTAG>"},
{v1::timelines_home, "/api/v1/timelines/home"},
{v1::timelines_list_list_id, "/api/v1/timelines/list/list/id"},
{v1::timelines_list_list_id, "/api/v1/timelines/list/list/<ID>"},
{v1::conversations, "/api/v1/conversations"},
{v1::conversations_id, "/api/v1/conversations/id"},
{v1::conversations_id, "/api/v1/conversations/<ID>"},
{v1::conversations_id_read, "/api/v1/conversations/<ID>/read"},
{v1::lists, "/api/v1/lists"},
@ -131,7 +131,7 @@ const map<API::endpoint_type,string_view> API::_endpoint_map
{v1::streaming_direct, "/api/v1/streaming/direct"},
{v1::notifications, "/api/v1/notifications"},
{v1::notifications_id, "/api/v1/notifications/id"},
{v1::notifications_id, "/api/v1/notifications/<ID>"},
{v1::notifications_clear, "/api/v1/notifications/clear"},
{v1::notifications_id_dismiss, "/api/v1/notifications/<ID>/dismiss"},
@ -148,7 +148,7 @@ const map<API::endpoint_type,string_view> API::_endpoint_map
{v1::custom_emojis, "/api/v1/custom/emojis"},
{v1::admin_accounts, "/api/v1/admin/accounts"},
{v1::admin_accounts_id, "/api/v1/admin/accounts/id"},
{v1::admin_accounts_id, "/api/v1/admin/accounts/<ID>"},
{v1::admin_accounts_account_id_action,
"/api/v1/admin/accounts/account/<ID>/action"},
{v1::admin_accounts_id_approve, "/api/v1/admin/accounts/<ID>/approve"},
@ -157,11 +157,11 @@ const map<API::endpoint_type,string_view> API::_endpoint_map
{v1::admin_accounts_id_unsilence, "/api/v1/admin/accounts/<ID>/unsilence"},
{v1::admin_accounts_id_unsuspend, "/api/v1/admin/accounts/<ID>/unsuspend"},
{v1::admin_reports, "/api/v1/admin/reports"},
{v1::admin_reports_id, "/api/v1/admin/reports/id"},
{v1::admin_reports_id, "/api/v1/admin/reports/<ID>"},
{v1::admin_reports_id_assign_to_self,
"/api/v1/admin/reports/<ID>/assign/to/self"},
{v1::admin_reports_id_unassign, "/api/v1/admin/reports/<ID>/unassign"},
{v1::admin_reports_id_resolve, "/api/v1/admin/reports/resolve"},
{v1::admin_reports_id_resolve, "/api/v1/admin/reports/<ID>/resolve"},
{v1::admin_reports_id_reopen, "/api/v1/admin/reports/<ID>/reopen"},
{v1::pleroma_notifications_read, " /api/v1/pleroma/notifications/read"},
@ -171,7 +171,7 @@ const map<API::endpoint_type,string_view> API::_endpoint_map
{v1::pleroma_accounts_id_unsubscribe,
"/api/v1/pleroma/accounts/<ID>/unsubscribe"},
{v1::pleroma_accounts_id_favourites,
"/api/v1/pleroma/accounts/:id/favourites"},
"/api/v1/pleroma/accounts/<ID>/favourites"},
{v1::pleroma_accounts_update_avatar,
"/api/v1/pleroma/accounts/update_avatar"},
{v1::pleroma_accounts_update_banner,
@ -239,10 +239,9 @@ const map<API::endpoint_type,string_view> API::_endpoint_map
{pleroma::disable_account, "/api/pleroma/disable_account"},
{pleroma::account_register, "/api/pleroma/account/register"},
{pleroma::pleroma_notification_settings,
"/api/pleroma/pleroma/notification_settings"},
{pleroma::pleroma_healthcheck, "/api/pleroma/pleroma/healthcheck"},
{pleroma::pleroma_change_email, "/api/pleroma/pleroma/change_email"},
{pleroma::notification_settings, "/api/pleroma/notification_settings"},
{pleroma::healthcheck, "/api/pleroma/healthcheck"},
{pleroma::change_email, "/api/pleroma/change_email"},
};
} // namespace mastodonpp

View File

@ -61,6 +61,27 @@ answer_type Connection::post(const endpoint_variant &endpoint,
endpoint_to_uri(endpoint), parameters);
}
answer_type Connection::patch(const endpoint_variant &endpoint,
const parametermap &parameters)
{
return make_request(http_method::PATCH,
endpoint_to_uri(endpoint), parameters);
}
answer_type Connection::put(const endpoint_variant &endpoint,
const parametermap &parameters)
{
return make_request(http_method::PUT,
endpoint_to_uri(endpoint), parameters);
}
answer_type Connection::del(const endpoint_variant &endpoint,
const parametermap &parameters)
{
return make_request(http_method::DELETE,
endpoint_to_uri(endpoint), parameters);
}
string Connection::get_new_stream_contents()
{
buffer_mutex.lock();

View File

@ -111,20 +111,44 @@ answer_type CURLWrapper::make_request(const http_method &method, string uri,
}
case http_method::PATCH:
{
if (!parameters.empty())
{
curl_mime *mime{parameters_to_curl_mime(uri, parameters)};
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
code = curl_easy_setopt(_connection, CURLOPT_MIMEPOST, mime);
}
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
code = curl_easy_setopt(_connection, CURLOPT_CUSTOMREQUEST, "PATCH");
break;
}
case http_method::PUT:
{
if (!parameters.empty())
{
curl_mime *mime{parameters_to_curl_mime(uri, parameters)};
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
code = curl_easy_setopt(_connection, CURLOPT_MIMEPOST, mime);
}
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
code = curl_easy_setopt(_connection, CURLOPT_UPLOAD, 1L);
code = curl_easy_setopt(_connection, CURLOPT_CUSTOMREQUEST, "PUT");
break;
}
case http_method::DELETE:
{
if (!parameters.empty())
{
curl_mime *mime{parameters_to_curl_mime(uri, parameters)};
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
code = curl_easy_setopt(_connection, CURLOPT_MIMEPOST, mime);
}
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
code = curl_easy_setopt(_connection, CURLOPT_CUSTOMREQUEST, "DELETE");
break;
}
}
@ -293,6 +317,8 @@ bool CURLWrapper::replace_parameter_in_uri(string &uri,
{
uri.replace(pos, parameter.first.size() + 2,
get<string_view>(parameter.second));
debuglog << "Replaced :" << parameter.first << " in URI with "
<< get<string_view>(parameter.second) << '\n';
return true;
}
}
@ -339,12 +365,45 @@ void CURLWrapper::add_parameters_to_uri(string &uri,
}
}
void CURLWrapper::add_mime_part(curl_mime *mime,
string_view name, string_view data) const
{
curl_mimepart *part{curl_mime_addpart(mime)};
if (part == nullptr)
{
throw CURLException{"Could not build HTTP form."};
}
CURLcode code{curl_mime_name(part, name.data())};
if (code != CURLE_OK)
{
throw CURLException{code, "Could not build HTTP form."};
}
if (data.substr(0, 6) == "@file:")
{
const string_view filename{data.substr(6)};
code = curl_mime_filedata(part, filename.data());
}
else
{
code = curl_mime_data(part, data.data(), CURL_ZERO_TERMINATED);
}
if (code != CURLE_OK)
{
throw CURLException{code, "Could not build HTTP form."};
}
debuglog << "Set form part: " << name << " = " << data << '\n';
}
curl_mime *CURLWrapper::parameters_to_curl_mime(string &uri,
const parametermap &parameters)
{
debuglog << "Building HTTP form.\n";
curl_mime *mime{curl_mime_init(_connection)};
for (const auto &param : parameters)
{
if (replace_parameter_in_uri(uri, param))
@ -352,52 +411,16 @@ curl_mime *CURLWrapper::parameters_to_curl_mime(string &uri,
continue;
}
CURLcode code;
if (holds_alternative<string_view>(param.second))
{
curl_mimepart *part{curl_mime_addpart(mime)};
if (part == nullptr)
{
throw CURLException{"Could not build HTTP form."};
}
code = curl_mime_name(part, param.first.data());
if (code != CURLE_OK)
{
throw CURLException{code, "Could not build HTTP form."};
}
code = curl_mime_data(part, get<string_view>(param.second).data(),
CURL_ZERO_TERMINATED);
if (code != CURLE_OK)
{
throw CURLException{code, "Could not build HTTP form."};
}
debuglog << "Set form part: " << param.first << " = "
<< get<string_view>(param.second) << '\n';
add_mime_part(mime, param.first, get<string_view>(param.second));
}
else
{
for (const auto &arg : get<vector<string_view>>(param.second))
{
curl_mimepart *part{curl_mime_addpart(mime)};
if (part == nullptr)
{
throw CURLException{"Could not build HTTP form."};
}
const string name{string(param.first) += "[]"};
code = curl_mime_name(part, name.c_str());
if (code != CURLE_OK)
{
throw CURLException{code, "Could not build HTTP form."};
}
code = curl_mime_data(part, arg.data(), CURL_ZERO_TERMINATED);
if (code != CURLE_OK)
{
throw CURLException{code, "Could not build HTTP form."};
}
debuglog << "Set form part: " << name << " = " << arg << '\n';
const string_view name{string(param.first) += "[]"};
add_mime_part(mime, name, arg);
}
}
}