Merge branch 'develop' into main
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
tastytea 2020-01-08 14:15:14 +01:00
commit d4ed04f0cd
Signed by: tastytea
GPG Key ID: CFC39497F1B26E07
13 changed files with 186 additions and 45 deletions

View File

@ -13,7 +13,7 @@ trigger:
- tag
steps:
- name: GCC 8 on Debian buster
- name: GCC 8 / clang 7
image: debian:buster-slim
pull: always
environment:
@ -25,10 +25,16 @@ steps:
- rm /etc/apt/apt.conf.d/docker-clean
- alias apt-get='rm -f /var/cache/apt/archives/lock && apt-get'
- apt-get update -q
- apt-get install -qq build-essential cmake
- apt-get install -qq build-essential cmake clang
- apt-get install -qq catch libcurl4-openssl-dev
- rm -rf build && mkdir -p build && cd build
- cmake -G "Unix Makefiles" -DWITH_TESTS=YES ..
- cmake -G "Unix Makefiles" -DWITH_TESTS=YES -DWITH_EXAMPLES=YES ..
- make VERBOSE=1
- make install DESTDIR=install
- cd tests && ctest -V
- cd ../../
- rm -rf build && mkdir -p build && cd build
- CXX="clang++" cmake -G "Unix Makefiles" -DWITH_TESTS=YES -DWITH_EXAMPLES=YES ..
- make VERBOSE=1
- make install DESTDIR=install
- cd tests && ctest -V
@ -36,7 +42,7 @@ steps:
- name: debian-package-cache
path: /var/cache/apt/archives
- name: GCC 7 on Ubuntu bionic
- name: GCC 7 / clang 6
image: ubuntu:bionic
pull: always
environment:
@ -48,10 +54,16 @@ steps:
- rm /etc/apt/apt.conf.d/docker-clean
- alias apt-get='rm -f /var/cache/apt/archives/lock && apt-get'
- apt-get update -q
- apt-get install -qq build-essential cmake
- apt-get install -qq build-essential cmake clang
- apt-get install -qq catch libcurl4-openssl-dev
- rm -rf build && mkdir -p build && cd build
- cmake -G "Unix Makefiles" -DWITH_TESTS=YES ..
- cmake -G "Unix Makefiles" -DWITH_TESTS=YES -DWITH_EXAMPLES=YES ..
- make VERBOSE=1
- make install DESTDIR=install
- cd tests && ctest -V
- cd ../../
- rm -rf build && mkdir -p build && cd build
- CXX="clang++" cmake -G "Unix Makefiles" -DWITH_TESTS=YES -DWITH_EXAMPLES=YES ..
- make VERBOSE=1
- make install DESTDIR=install
- cd tests && ctest -V

View File

@ -6,6 +6,7 @@
:uri-mastodon-cpp: https://schlomp.space/tastytea/mastodon-cpp
:uri-reference: https://doc.schlomp.space/{project}/
:uri-gcc: https://gcc.gnu.org/
:uti-clang: https://clang.llvm.org/
:uri-cmake: https://cmake.org/
:uri-doxygen: http://www.doxygen.nl/
:uri-catch: https://github.com/catchorg/Catch2
@ -39,7 +40,7 @@ Have a look at the link:{uri-reference}[reference].
==== Dependencies
* Tested OS: Linux
* C++ compiler (tested: link:{uri-gcc}[GCC] 7/8/9)
* C++ compiler (tested: link:{uri-gcc}[GCC] 7/8/9, link:{uri-lang}[clang] 6/7)
* link:{uri-cmake}[CMake] (at least: 3.9)
* link:{uri-curl}[curl] (tested: 7.66 / 7.58)
* Optional

View File

@ -15,7 +15,7 @@
// Print information about an instance (/api/v1/instance).
#include <mastodonpp.hpp>
#include "mastodonpp.hpp"
#include <iostream>
#include <string>

View File

@ -24,12 +24,16 @@
#include <string>
#include <string_view>
#include <variant>
namespace mastodonpp
{
using std::string;
using std::string_view;
using std::variant;
using endpoint_variant = variant<API::endpoint_type,string>;
/*!
* @brief Represents a connection to an instance. Used for requests.
@ -51,25 +55,43 @@ public:
explicit Connection(Instance &instance);
/*!
* @brief Make a HTTP GET call.
* @brief Make a HTTP GET call with parameters.
*
* Example:
* @code
* auto answer{connection.get(mastodonpp::API::v1::accounts_id_followers,
* {
* {"id", "12"},
* {"limit", "10"}
* })};
* @endcode
*
* @param endpoint Endpoint as API::endpoint_type or `std::string`.
* @param parameters A map of parameters.
*
* @param endpoint Endpoint as API::endpoint_type, for example:
* `mastodonpp::API::v1::instance`.
*
* @since 0.1.0
*/
[[nodiscard]]
answer_type get(const API::endpoint_type &endpoint);
answer_type get(const endpoint_variant &endpoint,
const parametermap &parameters);
/*!
* @brief Make a HTTP GET call.
* Example:
* @code
* auto answer{connection.get("/api/v1/instance")};
* @endcode
*
* @param endpoint Endpoint as string, for example: "/api/v1/instance".
* @param endpoint Endpoint as API::endpoint_type or `std::string`.
*
* @since 0.1.0
*/
[[nodiscard]]
answer_type get(const string_view &endpoint);
inline answer_type get(const endpoint_variant &endpoint)
{
return get(endpoint, {});
}
private:
Instance &_instance;

View File

@ -114,15 +114,32 @@ public:
CURLWrapper& operator=(CURLWrapper &&other) noexcept = default;
/*!
* @brief Make a request.
* @brief Returns pointer to the CURL easy handle.
*
* @param method The HTTP method.
* @param uri The full URI.
* You can use this handle to set or modify curl options. For more
* information consult [curl_easy_setopt(3)]
* (https://curl.haxx.se/libcurl/c/curl_easy_setopt.html).
*
* @since 0.1.0
*/
inline CURL *get_curl_easy_handle()
{
return _connection;
}
protected:
/*!
* @brief Make a HTTP request.
*
* @param method The HTTP method.
* @param uri The full URI.
* @param parameters A map of parameters.
*
* @since 0.1.0
*/
[[nodiscard]]
answer_type make_request(const http_method &method, const string_view &uri);
answer_type make_request(const http_method &method, string uri,
const parametermap &parameters);
private:
CURL *_connection;

View File

@ -45,7 +45,7 @@
*
* Or compile your code with `g++ $(pkg-config --cflags --libs mastodonpp)`.
*
* @subsection Example
* @subsection example Example
*
* @code
* #include <mastodonpp/mastodonpp.hpp>
@ -67,27 +67,36 @@
* }
* @endcode
*
* @subsection input Input
*
* All text input is expected to be UTF-8.
*
* @section exceptions Exceptions
*
* Any unrecoverable libcurl error will be thrown as a
* mastodonpp::CURLException. Network errors will **not** be thrown, but
* reported via the return value.
*
* @section thread_safety Thread safety
*
* The first time you construct an @link mastodonpp::Instance Instance @endlink
* or @link mastodonpp::Connection Connection @endlink, [curl_global_init()]
* (https://curl.haxx.se/libcurl/c/curl_global_init.html) is called. When the
* last @link mastodonpp::Instance Instance @endlink or @link
* mastodonpp::Connection Connection @endlink is destroyed,
* [curl_global_cleanup()]
* (https://curl.haxx.se/libcurl/c/curl_global_cleanup.html) is called. Both
* are not thread safe.
*
* @example example01_instance_info.cpp
*/
/*!
* @brief C++ wrapper for the Mastodon %API.
*
* All text input is expected to be UTF-8.
*
* @since 0.1.0
*/
namespace mastodonpp
{
} // namespace mastodonpp
{} // namespace mastodonpp
#endif // MASTODONPP_HPP

View File

@ -4,7 +4,7 @@ find_package(CURL REQUIRED)
# Write version in header.
configure_file ("version.hpp.in"
"${PROJECT_BINARY_DIR}/version.hpp" @ONLY)
"${CMAKE_CURRENT_BINARY_DIR}/version.hpp" @ONLY)
add_library(${PROJECT_NAME})
@ -19,7 +19,7 @@ set_target_properties(${PROJECT_NAME} PROPERTIES
target_include_directories(${PROJECT_NAME}
PRIVATE
"$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/src>" # version.hpp
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>" # version.hpp
PUBLIC
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")

View File

@ -31,7 +31,7 @@ const map<API::endpoint_type,string_view> API::_endpoint_map
{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_id, "/api/v1/accounts/id"},
{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"},
{v1::accounts_id_following, "/api/v1/accounts/<ID>/following"},

View File

@ -19,21 +19,27 @@
namespace mastodonpp
{
using std::holds_alternative;
Connection::Connection(Instance &instance)
: _instance{instance}
, _baseuri{instance.get_baseuri()}
{}
answer_type Connection::get(const API::endpoint_type &endpoint)
answer_type Connection::get(const endpoint_variant &endpoint,
const parametermap &parameters)
{
return make_request(
http_method::GET,
string(_baseuri).append(API{endpoint}.to_string_view()));
}
string uri{[&]
{
if (holds_alternative<API::endpoint_type>(endpoint))
{
return string(_baseuri).append(
API{std::get<API::endpoint_type>(endpoint)}.to_string_view());
}
answer_type Connection::get(const string_view &endpoint)
{
return make_request(http_method::GET, string(_baseuri).append(endpoint));
return std::get<string>(endpoint);
}()};
return make_request(http_method::GET, uri, parameters);
}
} // namespace mastodonpp

View File

@ -17,16 +17,26 @@
#include "curl_wrapper.hpp"
#include "exceptions.hpp"
#include "log.hpp"
#include "version.hpp"
#include <algorithm>
#include <array>
#include <atomic>
#include <cstdint>
#include <cstring>
namespace mastodonpp
{
using std::get;
using std::holds_alternative;
using std::any_of;
using std::array;
using std::atomic;
using std::uint8_t;
using std::uint16_t;
static uint16_t curlwrapper_instances{0};
static atomic<uint16_t> curlwrapper_instances{0};
CURLWrapper::CURLWrapper()
: _curl_buffer_error{}
@ -53,11 +63,9 @@ CURLWrapper::~CURLWrapper() noexcept
}
}
answer_type CURLWrapper::make_request(const http_method &method,
const string_view &uri)
answer_type CURLWrapper::make_request(const http_method &method, string uri,
const parametermap &parameters)
{
debuglog << "Making request to: " << uri << '\n';
CURLcode code;
switch (method)
{
@ -65,6 +73,52 @@ answer_type CURLWrapper::make_request(const http_method &method,
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
code = curl_easy_setopt(_connection, CURLOPT_HTTPGET, 1L);
for (const auto &param : parameters)
{
static constexpr array replace_in_uri
{
"id", "nickname", "nickname_or_id",
"hashtag", "permission_group"
};
if (any_of(replace_in_uri.begin(), replace_in_uri.end(),
[&param](const auto &s) { return s == param.first; }))
{
const auto pos{uri.find('<')};
if (pos != string::npos)
{
uri.replace(pos, param.first.size() + 2,
get<string>(param.second));
}
continue;
}
static bool first{true};
if (first)
{
uri.append("?");
first = false;
}
else
{
uri.append("&");
}
if (holds_alternative<string>(param.second))
{
uri.append(param.first + '=' + get<string>(param.second));
}
else
{
for (const auto &arg : get<vector<string>>(param.second))
{
uri.append(param.first + "[]=" + arg);
if (arg != *get<vector<string>>(param.second).rbegin())
{
uri.append("&");
}
}
}
}
break;
}
case http_method::POST:
@ -97,6 +151,7 @@ answer_type CURLWrapper::make_request(const http_method &method,
throw CURLException{code, "Failed to set HTTP method",
_curl_buffer_error};
}
debuglog << "Making request to: " << uri << '\n';
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
code = curl_easy_setopt(_connection, CURLOPT_URL, uri.data());
@ -180,6 +235,25 @@ void CURLWrapper::setup_curl()
throw CURLException{code, "Failed to set write data",
_curl_buffer_error};
}
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
code = curl_easy_setopt(_connection, CURLOPT_USERAGENT,
string("mastorss/").append(version).c_str());
if (code != CURLE_OK)
{
throw CURLException{code, "Failed to set User-Agent",
_curl_buffer_error};
}
// The next 2 only fail if HTTP is not supported.
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
code = curl_easy_setopt(_connection, CURLOPT_FOLLOWLOCATION, 1L);
if (code != CURLE_OK)
{
throw CURLException{code, "HTTP is not supported.", _curl_buffer_error};
}
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
curl_easy_setopt(_connection, CURLOPT_MAXREDIRS, 10L);
}
} // namespace mastodonpp

View File

@ -40,7 +40,7 @@ uint64_t Instance::get_max_chars()
{
debuglog << "Querying " << _hostname << " for max_toot_chars…\n";
const auto answer{make_request(http_method::GET,
_baseuri + "/api/v1/instance")};
_baseuri + "/api/v1/instance", {})};
if (!answer)
{
debuglog << "Could not get instance info.\n";

View File

@ -31,7 +31,7 @@ constexpr auto shorten_filename(const string_view &filename)
{
for (const string_view &dir : {"/src/", "/include/"})
{
auto pos{filename.rfind("/src/")};
const auto pos{filename.rfind(dir)};
if (pos != string_view::npos)
{
return filename.substr(pos + dir.size());

View File

@ -8,7 +8,7 @@ namespace mastodonpp
using std::string_view;
static constexpr string_view version = "@PROJECT_VERSION@";
static constexpr string_view version{"@PROJECT_VERSION@"};
} // namespace mastodonpp