diff --git a/README.adoc b/README.adoc index 56f4e53..f272b2c 100644 --- a/README.adoc +++ b/README.adoc @@ -11,6 +11,7 @@ :uri-catch: https://github.com/catchorg/Catch2 :uri-dpkg: https://packages.qa.debian.org/dpkg :uri-rpm-build: http://www.rpm.org +:uri-curl: https://curl.haxx.se/ *{project}* is a C++ wrapper for the Mastodon API. It replaces link:{uri-mastodon-cpp}[mastodon-cpp]. @@ -40,6 +41,7 @@ Have a look at the link:{uri-reference}[reference]. * Tested OS: Linux * C++ compiler (tested: link:{uri-gcc}[GCC] 7/8/9) * link:{uri-cmake}[CMake] (at least: 3.9) +* link:{uri-curl}[curl] (tested: 7.66 / 7.58) * Optional ** Documentation: link:{uri-doxygen}[Doxygen] (tested: 1.8) ** Tests: link:{uri-catch}[Catch] (tested: 2.5 / 1.2) diff --git a/include/api.hpp b/include/api.hpp index 72ca3f7..00a6dc3 100644 --- a/include/api.hpp +++ b/include/api.hpp @@ -24,11 +24,14 @@ namespace mastodonpp { +using std::map; using std::string_view; using std::variant; /*! - * @brief Holds API endpoints. + * @brief Holds %API endpoints. + * + * Supported %API endpoints: Mastodon 3.0.1, Pleroma 1.1.7. * * @since 0.1.0 * @@ -46,7 +49,154 @@ public: */ enum class v1 { - instance + apps, + apps_verify_credentials, + + accounts, + accounts_verify_credentials, + accounts_update_credentials, + accounts_id, + accounts_id_statuses, + accounts_id_followers, + accounts_id_following, + accounts_id_lists, + accounts_id_identity_proofs, + accounts_id_follow, + accounts_id_unfollow, + accounts_id_block, + accounts_id_unblock, + accounts_id_mute, + accounts_id_unmute, + accounts_id_pin, + accounts_id_unpin, + accounts_relationships, + accounts_search, + + bookmarks, + + favourites, + + mutes, + + blocks, + + domain_blocks, + + filters, + filters_id, + + reports, + + follow_requests, + follow_requests_id_authorize, + follow_requests_id_reject, + + endorsements, + + featured_tags, + featured_tags_id, + featured_tags_suggestions, + + preferences, + + suggestions, + suggestions_account_id, + + statuses, + statuses_id, + statuses_id_context, + statuses_id_reblogged_by, + statuses_id_favourited_by, + statuses_id_favourite, + statuses_id_unfavourite, + statuses_id_reblog, + statuses_id_unreblog, + statuses_id_bookmark, + statuses_id_unbookmark, + statuses_id_mute, + statuses_id_unmute, + statuses_id_pin, + statuses_id_unpin, + + media, + media_id, + + polls_id, + polls_id_votes, + + scheduled_statuses, + scheduled_statuses_id, + + timelines_public, + timelines_tag_hashtag, + timelines_home, + timelines_list_list_id, + + conversations, + conversations_id, + conversations_id_read, + + lists, + lists_id, + lists_id_accounts, + + markers, + + streaming_health, + streaming_user, + streaming_public, + streaming_public_local, + streaming_hashtag, + streaming_hashtag_local, + streaming_list, + streaming_direct, + + notifications, + notifications_id, + notifications_clear, + notifications_id_dismiss, + + push_subscription, + + instance, + instance_peers, + instance_activity, + + trends, + + directory, + + custom_emojis, + + admin_accounts, + admin_accounts_id, + admin_accounts_account_id_action, + admin_accounts_id_approve, + admin_accounts_id_reject, + admin_accounts_id_enable, + admin_accounts_id_unsilence, + admin_accounts_id_unsuspend, + admin_reports, + admin_reports_id, + admin_reports_id_assign_to_self, + admin_reports_id_unassign, + admin_reports_id_resolve, + admin_reports_id_reopen, + + pleroma_notifications_read, + + pleroma_accounts_id_subscribe, + pleroma_accounts_id_unsubscribe, + pleroma_accounts_id_favourites, + pleroma_accounts_update_avatar, + pleroma_accounts_update_banner, + pleroma_accounts_update_background, + pleroma_accounts_confirmation_resend, + + pleroma_mascot, + + pleroma_conversations_id_statuses, + pleroma_conversations_id, }; /*! @@ -62,11 +212,87 @@ public: }; /*! - * @brief Type for endpoints. Either API::v1 or API::v2. + * @brief An enumeration of all oauth %API endpoints. + * + * The original `/` are substituted with `_`. * * @since 0.1.0 */ - using endpoint_type = variant; + enum class oauth + { + authorize, + token, + revoke + }; + + /*! + * @brief An enumeration of all other %API endpoints. + * + * These endpoints are directly under `/api/`. + * + * The original `/` are substituted with `_`. + * + * @since 0.1.0 + */ + enum class other + { + proofs, + oembed + }; + + /*! + * @brief An enumeration of all pleroma %API endpoints. + * + * The original `/` are substituted with `_`. + * + * @since 0.1.0 + */ + enum class pleroma + { + admin_users, + admin_users_follow, + admin_users_unfollow, + admin_users_nickname, + admin_users_tag, + admin_users_nickname_permission_group, + admin_users_nickname_permission_group_permission_group, + admin_users_nickname_activation_status, + admin_users_nickname_or_id, + admin_users_nickname_or_id_statuses, + admin_relay, + admin_users_invite_token, + admin_users_invites, + admin_users_revoke_invite, + admin_users_email_invite, + admin_users_nickname_password_reset, + admin_reports, + admin_reports_id, + admin_reports_id_respond, + admin_statuses_id, + admin_config_migrate_to_db, + admin_config_migrate_from_db, + admin_config, + + emoji, + follow_import, + captcha, + + delete_account, + disable_account, + account_register, + + pleroma_notification_settings, + pleroma_healthcheck, + pleroma_change_email + }; + + /*! + * @brief Type for endpoints. Can be API::v1, API::v2, API::oauth, + * API::other or API::pleroma. + * + * @since 0.1.0 + */ + using endpoint_type = variant; /*! * @brief Constructs an API object. You should never need this. @@ -76,7 +302,7 @@ public: * * @since 0.1.0 */ - explicit API(const endpoint_type &endpoint); + explicit API(); /*! * @brief Convert #endpoint_type to `std::string_view`. @@ -84,10 +310,14 @@ public: * @since 0.1.0 */ [[nodiscard]] - string_view to_string_view() const; + inline string_view endpoint_to_string_view(const endpoint_type &endpoint) + const + { + return _endpoint_map.at(endpoint).data(); + } private: - const endpoint_type _endpoint; + const map _endpoint_map; }; } // namespace mastodonpp diff --git a/include/connection.hpp b/include/connection.hpp index a26120c..1a6c11f 100644 --- a/include/connection.hpp +++ b/include/connection.hpp @@ -74,6 +74,7 @@ public: private: Instance &_instance; const string_view _baseuri; + const API _api; }; } // namespace mastodonpp diff --git a/include/curl_wrapper.hpp b/include/curl_wrapper.hpp index 77eda31..07603b2 100644 --- a/include/curl_wrapper.hpp +++ b/include/curl_wrapper.hpp @@ -38,7 +38,10 @@ using std::string_view; enum class http_method { GET, - POST + POST, + PATCH, + PUT, + DELETE }; /*! diff --git a/src/api.cpp b/src/api.cpp index 5168e83..98a1d7f 100644 --- a/src/api.cpp +++ b/src/api.cpp @@ -16,25 +16,229 @@ #include "api.hpp" -#include - namespace mastodonpp { -using std::map; -using std::string_view; +API::API() + : _endpoint_map +{ + {v1::apps, "/api/v1/apps"}, + {v1::apps_verify_credentials, "/api/v1/apps/verify/credentials"}, -API::API(const endpoint_type &endpoint) - : _endpoint{endpoint} + {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_statuses, "/api/v1/accounts//statuses"}, + {v1::accounts_id_followers, "/api/v1/accounts//followers"}, + {v1::accounts_id_following, "/api/v1/accounts//following"}, + {v1::accounts_id_lists, "/api/v1/accounts//lists"}, + {v1::accounts_id_identity_proofs, "/api/v1/accounts//identity/proofs"}, + {v1::accounts_id_follow, "/api/v1/accounts//follow"}, + {v1::accounts_id_unfollow, "/api/v1/accounts//unfollow"}, + {v1::accounts_id_block, "/api/v1/accounts//block"}, + {v1::accounts_id_unblock, "/api/v1/accounts//unblock"}, + {v1::accounts_id_mute, "/api/v1/accounts//mute"}, + {v1::accounts_id_unmute, "/api/v1/accounts//unmute"}, + {v1::accounts_id_pin, "/api/v1/accounts//pin"}, + {v1::accounts_id_unpin, "/api/v1/accounts//unpin"}, + {v1::accounts_relationships, "/api/v1/accounts/relationships"}, + {v1::accounts_search, "/api/v1/accounts/search"}, + + {v1::bookmarks, "/api/v1/bookmarks"}, + + {v1::favourites, "/api/v1/favourites"}, + + {v1::mutes, "/api/v1/mutes"}, + + {v1::blocks, "/api/v1/blocks"}, + + {v1::domain_blocks, "/api/v1/domain/blocks"}, + + {v1::filters, "/api/v1/filters"}, + {v1::filters_id, "/api/v1/filters/id"}, + + {v1::reports, "/api/v1/reports"}, + + {v1::follow_requests, "/api/v1/follow/requests"}, + {v1::follow_requests_id_authorize, + "/api/v1/follow/requests//authorize"}, + {v1::follow_requests_id_reject, "/api/v1/follow/requests//reject"}, + + {v1::endorsements, "/api/v1/endorsements"}, + + {v1::featured_tags, "/api/v1/featured/tags"}, + {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::statuses, "/api/v1/statuses"}, + {v1::statuses_id, "/api/v1/statuses/id"}, + {v1::statuses_id_context, "/api/v1/statuses//context"}, + {v1::statuses_id_reblogged_by, "/api/v1/statuses//reblogged/by"}, + {v1::statuses_id_favourited_by, "/api/v1/statuses//favourited/by"}, + {v1::statuses_id_favourite, "/api/v1/statuses//favourite"}, + {v1::statuses_id_unfavourite, "/api/v1/statuses//unfavourite"}, + {v1::statuses_id_reblog, "/api/v1/statuses//reblog"}, + {v1::statuses_id_unreblog, "/api/v1/statuses//unreblog"}, + {v1::statuses_id_bookmark, "/api/v1/statuses//bookmark"}, + {v1::statuses_id_unbookmark, "/api/v1/statuses//unbookmark"}, + {v1::statuses_id_mute, "/api/v1/statuses//mute"}, + {v1::statuses_id_unmute, "/api/v1/statuses//unmute"}, + {v1::statuses_id_pin, "/api/v1/statuses//pin"}, + {v1::statuses_id_unpin, "/api/v1/statuses//unpin"}, + + {v1::media, "/api/v1/media"}, + {v1::media_id, "/api/v1/media/id"}, + + {v1::polls_id, "/api/v1/polls/id"}, + {v1::polls_id_votes, "/api/v1/polls//votes"}, + + {v1::scheduled_statuses, "/api/v1/scheduled/statuses"}, + {v1::scheduled_statuses_id, "/api/v1/scheduled/statuses/id"}, + + {v1::timelines_public, "/api/v1/timelines/public"}, + {v1::timelines_tag_hashtag, "/api/v1/timelines/tag/"}, + {v1::timelines_home, "/api/v1/timelines/home"}, + {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_read, "/api/v1/conversations//read"}, + + {v1::lists, "/api/v1/lists"}, + {v1::lists_id, "/api/v1/lists/id"}, + {v1::lists_id_accounts, "/api/v1/lists//accounts"}, + + {v1::markers, "/api/v1/markers"}, + + {v1::streaming_health, "/api/v1/streaming/health"}, + {v1::streaming_user, "/api/v1/streaming/user"}, + {v1::streaming_public, "/api/v1/streaming/public"}, + {v1::streaming_public_local, "/api/v1/streaming/public/local"}, + {v1::streaming_hashtag, "/api/v1/streaming/hashtag"}, + {v1::streaming_hashtag_local, "/api/v1/streaming/hashtag/local"}, + {v1::streaming_list, "/api/v1/streaming/list"}, + {v1::streaming_direct, "/api/v1/streaming/direct"}, + + {v1::notifications, "/api/v1/notifications"}, + {v1::notifications_id, "/api/v1/notifications/id"}, + {v1::notifications_clear, "/api/v1/notifications/clear"}, + {v1::notifications_id_dismiss, "/api/v1/notifications//dismiss"}, + + {v1::push_subscription, "/api/v1/push/subscription"}, + + {v1::instance, "/api/v1/instance"}, + {v1::instance_peers, "/api/v1/instance/peers"}, + {v1::instance_activity, "/api/v1/instance/activity"}, + + {v1::trends, "/api/v1/trends"}, + + {v1::directory, "/api/v1/directory"}, + + {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_account_id_action, + "/api/v1/admin/accounts/account//action"}, + {v1::admin_accounts_id_approve, "/api/v1/admin/accounts//approve"}, + {v1::admin_accounts_id_reject, "/api/v1/admin/accounts//reject"}, + {v1::admin_accounts_id_enable, "/api/v1/admin/accounts//enable"}, + {v1::admin_accounts_id_unsilence, "/api/v1/admin/accounts//unsilence"}, + {v1::admin_accounts_id_unsuspend, "/api/v1/admin/accounts//unsuspend"}, + {v1::admin_reports, "/api/v1/admin/reports"}, + {v1::admin_reports_id, "/api/v1/admin/reports/id"}, + {v1::admin_reports_id_assign_to_self, + "/api/v1/admin/reports//assign/to/self"}, + {v1::admin_reports_id_unassign, "/api/v1/admin/reports//unassign"}, + {v1::admin_reports_id_resolve, "/api/v1/admin/reports/resolve"}, + {v1::admin_reports_id_reopen, "/api/v1/admin/reports//reopen"}, + + {v1::pleroma_notifications_read, " /api/v1/pleroma/notifications/read"}, + + {v1::pleroma_accounts_id_subscribe, + "/api/v1/pleroma/accounts//subscribe"}, + {v1::pleroma_accounts_id_unsubscribe, + "/api/v1/pleroma/accounts//unsubscribe"}, + {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, + "/api/v1/pleroma/accounts/update_banner"}, + {v1::pleroma_accounts_update_background, + "/api/v1/pleroma/accounts/update_background"}, + {v1::pleroma_accounts_confirmation_resend, + "/api/v1/pleroma/accounts/confirmation_resend"}, + + {v1::pleroma_mascot, "/api/v1/pleroma/mascot"}, + + {v1::pleroma_conversations_id_statuses, + "/api/v1/pleroma/conversations//statuses"}, + {v1::pleroma_conversations_id, "/api/v1/pleroma/conversations/"}, + + {v2::search, "/api/v2/search"}, + + {oauth::authorize, "/oauth/authorize"}, + {oauth::token, "/oauth/token"}, + {oauth::revoke, "/oauth/revoke"}, + + {other::proofs, "/api/proofs"}, + {other::oembed, "/api/oembed"}, + + {pleroma::admin_users, "/api/pleroma/admin/users"}, + {pleroma::admin_users_follow, "/api/pleroma/admin/users/follow"}, + {pleroma::admin_users_unfollow, "/api/pleroma/admin/users/unfollow"}, + {pleroma::admin_users_nickname, "/api/pleroma/admin/users/"}, + {pleroma::admin_users_tag, "/api/pleroma/admin/users/tag"}, + {pleroma::admin_users_nickname_permission_group, + "/api/pleroma/admin/users//permission_group"}, + {pleroma::admin_users_nickname_permission_group_permission_group, + "/api/pleroma/admin/users//permission_group/"}, + {pleroma::admin_users_nickname_activation_status, + "/api/pleroma/admin/users//activation_status"}, + {pleroma::admin_users_nickname_or_id, + "/api/pleroma/admin/users/"}, + {pleroma::admin_users_nickname_or_id_statuses, + "/api/pleroma/admin/users//statuses"}, + {pleroma::admin_relay, "/api/pleroma/admin/relay"}, + {pleroma::admin_users_invite_token, + "/api/pleroma/admin/users/invite_token"}, + {pleroma::admin_users_invites, "/api/pleroma/admin/users/invites"}, + {pleroma::admin_users_revoke_invite, + "/api/pleroma/admin/users/revoke_invite"}, + {pleroma::admin_users_email_invite, + "/api/pleroma/admin/users/email_invite"}, + {pleroma::admin_users_nickname_password_reset, + "/api/pleroma/admin/users//password_reset"}, + {pleroma::admin_reports, "/api/pleroma/admin/reports"}, + {pleroma::admin_reports_id, "/api/pleroma/admin/reports/"}, + {pleroma::admin_reports_id_respond, + "/api/pleroma/admin/reports//respond"}, + {pleroma::admin_statuses_id, "/api/pleroma/admin/statuses/"}, + {pleroma::admin_config_migrate_to_db, + "/api/pleroma/admin/config/migrate_to_db"}, + {pleroma::admin_config_migrate_from_db, + "/api/pleroma/admin/config/migrate_from_db"}, + {pleroma::admin_config, "/api/pleroma/admin/config"}, + + {pleroma::emoji, "/api/pleroma/emoji"}, + {pleroma::follow_import, "/api/pleroma/follow_import"}, + {pleroma::captcha, "/api/pleroma/captcha,"}, + {pleroma::delete_account, "/api/pleroma/delete_account"}, + {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"}, +} {} -string_view API::to_string_view() const -{ - static const map endpoint_map - { - {v1::instance, "/api/v1/instance"}, - {v2::search, "/api/v2/search"} - }; - return endpoint_map.at(_endpoint).data(); -} } // namespace mastodonpp diff --git a/src/connection.cpp b/src/connection.cpp index e6bca95..cc956e0 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -22,13 +22,14 @@ namespace mastodonpp Connection::Connection(Instance &instance) : _instance{instance} , _baseuri{instance.get_baseuri()} + , _api{} {} answer_type Connection::get(const API::endpoint_type &endpoint) { return make_request( http_method::GET, - string(_baseuri).append(API{endpoint}.to_string_view())); + string(_baseuri).append(_api.endpoint_to_string_view(endpoint))); } answer_type Connection::get(const string_view &endpoint) diff --git a/src/curl_wrapper.cpp b/src/curl_wrapper.cpp index 0ff6364..130a27f 100644 --- a/src/curl_wrapper.cpp +++ b/src/curl_wrapper.cpp @@ -59,6 +59,24 @@ answer_type CURLWrapper::make_request(const http_method &method, code = curl_easy_setopt(_connection, CURLOPT_POST, 1L); break; } + case http_method::PATCH: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + code = curl_easy_setopt(_connection, CURLOPT_CUSTOMREQUEST, "PATCH"); + break; + } + case http_method::PUT: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + code = curl_easy_setopt(_connection, CURLOPT_UPLOAD, 1L); + break; + } + case http_method::DELETE: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + code = curl_easy_setopt(_connection, CURLOPT_CUSTOMREQUEST, "DELETE"); + break; + } } if (code != CURLE_OK) {