-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #10 from nxxm/feature/pagination-support
Pagination support
- Loading branch information
Showing
16 changed files
with
679 additions
and
109 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,132 @@ | ||
{ | ||
"nxxm/xxhr" : { "@" : "feature/move-to-native-tipi-deps" } | ||
, "cpp-pre/json" : { "@" : "feature/move-to-native-tipi-deps" } | ||
, "cpp-pre/type_traits": {} | ||
"cpp-pre/json": { | ||
"@": "feature/move-to-native-tipi-deps", | ||
"requires": { | ||
"boostorg/boost": { | ||
"@": "boost-1.80.0", | ||
"opts": "set(BOOST_INCLUDE_LIBRARIES system filesystem uuid)", | ||
"packages": [ | ||
"boost_system", | ||
"boost_filesystem", | ||
"boost_uuid" | ||
], | ||
"targets": [ | ||
"Boost::system", | ||
"Boost::filesystem", | ||
"Boost::uuid" | ||
], | ||
"u": true | ||
}, | ||
"nlohmann/json": { | ||
"@": "v3.11.2", | ||
"u": false, | ||
"x": [ | ||
"benchmarks", | ||
"/tests", | ||
"/docs", | ||
"/tools" | ||
] | ||
} | ||
}, | ||
"u": false | ||
}, | ||
"cpp-pre/type_traits": { | ||
"@": "v2.0.0", | ||
"u": false, | ||
"x": [ | ||
"/test" | ||
] | ||
}, | ||
"nxxm/xxhr": { | ||
"@": ":faa17ac3547ad194d916ea69ac3fd479202629da", | ||
"requires": { | ||
"aantron/better-enums": { | ||
"@": "0.11.1", | ||
"u": false, | ||
"x": [ | ||
"/example", | ||
"/script", | ||
"/doc", | ||
"/test" | ||
] | ||
}, | ||
"boostorg/boost": { | ||
"@": "boost-1.80.0", | ||
"opts": "set(BOOST_INCLUDE_LIBRARIES system filesystem uuid)", | ||
"packages": [ | ||
"boost_system", | ||
"boost_filesystem", | ||
"boost_uuid" | ||
], | ||
"targets": [ | ||
"Boost::system", | ||
"Boost::filesystem", | ||
"Boost::uuid" | ||
], | ||
"u": true | ||
}, | ||
"nxxm/curl": { | ||
"@": ":eee4ae62ee24aec9c7f8948fd8670a5e80c2cf83", | ||
"opts": "set(BUILD_CURL_TESTS OFF) \nset(BUILD_CURL_EXE ON) \nset(CMAKE_USE_OPENSSL ON) \nset(CMAKE_USE_LIBSSH2 OFF) \nset(BUILD_TESTING OFF)", | ||
"packages": [ | ||
"CURL" | ||
], | ||
"requires": { | ||
"hunter-packages/c-ares": { | ||
"@": "v1.14.0-p0", | ||
"packages": [ | ||
"c-ares" | ||
], | ||
"targets": [ | ||
"c-ares::cares" | ||
], | ||
"u": true | ||
}, | ||
"hunter-packages/zlib": { | ||
"@": "v1.2.11-p1", | ||
"packages": [ | ||
"ZLIB" | ||
], | ||
"targets": [ | ||
"ZLIB::zlib" | ||
], | ||
"u": true | ||
}, | ||
"nxxm/boringssl": { | ||
"@": ":358175c062c3a3964d4734df4b122e6af851def0", | ||
"u": true, | ||
"packages": [ | ||
"OpenSSL", | ||
"BoringSSL" | ||
], | ||
"targets": [ | ||
"OpenSSL::SSL", | ||
"OpenSSL::Crypto", | ||
"BoringSSL::decrepit" | ||
], | ||
"find_mode": " " | ||
} | ||
}, | ||
"targets": [ | ||
"CURL::libcurl" | ||
], | ||
"u": true | ||
} | ||
} | ||
}, | ||
"boostorg/boost": { | ||
"@": "boost-1.80.0", | ||
"opts": "set(BOOST_INCLUDE_LIBRARIES system filesystem uuid)", | ||
"packages": [ | ||
"boost_system", | ||
"boost_filesystem", | ||
"boost_uuid" | ||
], | ||
"targets": [ | ||
"Boost::system", | ||
"Boost::filesystem", | ||
"Boost::uuid" | ||
], | ||
"u": true | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
#pragma once | ||
|
||
#include <string> | ||
#include <map> | ||
#include <xxhr/xxhr.hpp> | ||
#include <xxhr/util.hpp> | ||
#include <boost/algorithm/string.hpp> | ||
#include <boost/algorithm/string/split.hpp> | ||
|
||
namespace gh::detail::pagination { | ||
using namespace std::string_literals; | ||
|
||
const size_t MAX_PAGE_SIZE = 100; | ||
const size_t MIN_PAGE_SIZE = 5; | ||
const size_t DEFAULT_PAGE_SIZE = 30; | ||
|
||
const std::string PAGE_LINK_FIRST = "first"; | ||
const std::string PAGE_LINK_LAST = "last"; | ||
const std::string PAGE_LINK_NEXT = "next"; | ||
const std::string PAGE_LINK_PREVIOUS = "prev"; | ||
|
||
const std::string HEADER_NAME_PAGE_LINKS = "link"; | ||
|
||
inline bool has_gh_pagination_links(const xxhr::Response &resp) { | ||
return resp.header.find(HEADER_NAME_PAGE_LINKS) != resp.header.end(); | ||
} | ||
|
||
inline std::map<std::string, std::string> parse_gh_pagination_header(std::string header_value) { | ||
// as per doc https://docs.github.com/en/rest/using-the-rest-api/using-pagination-in-the-rest-api?apiVersion=2022-11-28, the header comes as: | ||
// link: <https://api.github.com/repositories/1300192/issues?page=2>; rel="prev", <https://api.github.com/repositories/1300192/issues?page=4>; rel="next", <https://api.github.com/repositories/1300192/issues?page=515>; rel="last", <https://api.github.com/repositories/1300192/issues?page=1>; rel="first" | ||
std::map<std::string, std::string> result; | ||
|
||
std::vector<std::string> link_parts; | ||
boost::split(link_parts, header_value, boost::is_any_of(",")); | ||
|
||
for(auto lp : link_parts) { | ||
|
||
size_t pos_semicolon = lp.find(';', 0); | ||
|
||
std::string url_fragment = lp.substr(0, pos_semicolon); // should look like ' <https://api.github.com/repositories/1300192/issues?page=2>' | ||
boost::trim_all_if(url_fragment, boost::is_any_of("<> ")); | ||
|
||
|
||
std::string rel_fragment = lp.substr(pos_semicolon + 1 /* skip the semicolon */); | ||
boost::trim_all(rel_fragment); // should now look like 'rel="page-link-name"' | ||
|
||
static const std::string rel_prefix = "rel="; | ||
if(boost::starts_with(rel_fragment, rel_prefix)) { | ||
rel_fragment = rel_fragment.substr(rel_prefix.length()); | ||
boost::trim_if(rel_fragment, boost::is_any_of("\"")); | ||
} | ||
else { | ||
throw std::runtime_error("Cannot parse malformed GH pagination header: link: "s + header_value); | ||
} | ||
|
||
result.insert({ rel_fragment, url_fragment }); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
//! \brief returns the next page URL or nullopt given a xxhr Response | ||
inline std::optional<std::string> get_next_page_url(const xxhr::Response &resp) { | ||
|
||
if(has_gh_pagination_links(resp)) { | ||
auto links = parse_gh_pagination_header(resp.header.at(HEADER_NAME_PAGE_LINKS)); | ||
auto next_page_it = links.find(PAGE_LINK_NEXT); | ||
if(next_page_it != links.end()) { | ||
return next_page_it->second; | ||
} | ||
} | ||
|
||
return std::nullopt; | ||
} | ||
|
||
const std::string QUERY_STRING_PER_PAGE_KEY = "per_page"; | ||
|
||
//! \brief get the query string fragment for paginated urls | ||
inline std::string get_per_page_query_string(size_t per_page = MAX_PAGE_SIZE) { | ||
return QUERY_STRING_PER_PAGE_KEY + "="s + std::to_string(per_page); | ||
} | ||
|
||
} |
Oops, something went wrong.