From afb5ebabf55c5c806e221ad15fca2fa0d5ef6d2b Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 6 Mar 2018 08:30:08 +0000 Subject: Initial commit, WIP --- service/Jamfile.jam | 64 ++++++ service/api.ice | 19 ++ service/apiImpl.cpp | 91 ++++++++ service/apiImpl.h | 25 ++ service/data.sql | 3 + .../filesearching/xtrans-1.3.5.tar.bz2.html | 251 +++++++++++++++++++++ .../fixtures/filesearching/zstd-1.3.3.tar.gz.html | 243 ++++++++++++++++++++ service/main.cpp | 18 ++ service/models.ice | 25 ++ service/schema.sql | 8 + service/sql/getServices.sql | 3 + service/test.cpp | 77 +++++++ 12 files changed, 827 insertions(+) create mode 100644 service/Jamfile.jam create mode 100644 service/api.ice create mode 100644 service/apiImpl.cpp create mode 100644 service/apiImpl.h create mode 100644 service/data.sql create mode 100644 service/fixtures/filesearching/xtrans-1.3.5.tar.bz2.html create mode 100644 service/fixtures/filesearching/zstd-1.3.3.tar.gz.html create mode 100644 service/main.cpp create mode 100644 service/models.ice create mode 100644 service/schema.sql create mode 100644 service/sql/getServices.sql create mode 100644 service/test.cpp (limited to 'service') diff --git a/service/Jamfile.jam b/service/Jamfile.jam new file mode 100644 index 0000000..e7bfed9 --- /dev/null +++ b/service/Jamfile.jam @@ -0,0 +1,64 @@ +import icetray ; +import package ; +import testing ; + +lib boost_utf : : boost_unit_test_framework ; +lib dbpp-postgresql : : : : /usr/include/dbpp-postgresql ; +lib dryice : : : : /usr/include/icetray ; + +lib mirrorsearch : + [ glob *.cpp *.ice sql/*.sql : test.cpp ] + : + yes + ..//adhocutil + ..//dbppcore + ..//boost_system + ..//boost_filesystem + ..//boost_date_time + ..//Ice + ..//IceBox + ..//IceUtil + ..//pthread + ..//icetray + ..//slicer + ..//slicer-db + ..//glibmm + ..//xml2 + MirrorSearch + . + . + : : + . + ..//icetray + ; + +path-constant me : . ; + +run test.cpp + : : + data.sql + schema.sql + : + BOOST_TEST_DYN_LINK + ..//boost_system + ..//boost_filesystem + boost_utf + ROOT=\"$(me)\" + dbpp-postgresql + ..//dbppcore + ..//adhocutil + ..//boost_system + ..//boost_filesystem + ..//netfs-api + ..//IceUtil + ..//Ice + ..//IceBox + ..//pthread + dryice + mirrorsearch + mirrorsearch +; + +package.install install : : : mirrorsearch ; + + diff --git a/service/api.ice b/service/api.ice new file mode 100644 index 0000000..c207c3d --- /dev/null +++ b/service/api.ice @@ -0,0 +1,19 @@ +#ifndef MIRRORSEARCH_API +#define MIRRORSEARCH_API + +#include + +module MirrorSearch { + exception XmlError { + string msg; + }; + + interface Search { + idempotent SearchServices getServices(); + idempotent SearchHits getMatches(string filename); + idempotent optional(0) string feelingLucky(string filename); + }; +}; + +#endif + diff --git a/service/apiImpl.cpp b/service/apiImpl.cpp new file mode 100644 index 0000000..588d360 --- /dev/null +++ b/service/apiImpl.cpp @@ -0,0 +1,91 @@ +#include "apiImpl.h" + +#include +#include +#include + +#include +#include +#include +#include + +namespace MirrorSearch { + SearchImpl::SearchImpl(IceTray::DatabasePoolPtr db) : + IceTray::AbstractDatabaseClient(db), + log(LOGMANAGER()->getLogger()) + { + } + + SearchServices SearchImpl::getServices(const ::Ice::Current&) + { + return fetch(sql::getServices); + } + + typedef std::shared_ptr xmlDocSPtr; + typedef std::shared_ptr xmlXPathContextSPtr; + typedef std::shared_ptr xmlXPathObjectSPtr; + + static auto getDoc(const ::std::string & url, int flags) + { + if (auto doc = xmlDocSPtr(htmlReadFile(url.c_str(), NULL, flags), xmlFreeDoc)) { + return doc; + } + throw XmlError("Failed to open " + url); + } + + static auto getXPathCxt(const xmlDocSPtr & doc) + { + if (auto xpathCtx = xmlXPathContextSPtr(xmlXPathNewContext(doc.get()), xmlXPathFreeContext)) { + return xpathCtx; + } + throw XmlError("Failed to create xpath context"); + } + + static auto getXPathObj(const ::std::string & xpath, const xmlXPathContextSPtr & ctx, xmlXPathObjectType type) + { + if (auto xpathObj = xmlXPathObjectSPtr(xmlXPathEvalExpression(BAD_CAST xpath.c_str(), ctx.get()), xmlXPathFreeObject)) { + if (xpathObj->type != type) { + throw XmlError("Xpath evaluates to wrong type " + xpath); + } + return xpathObj; + } + throw XmlError("Failed to evaluate xpath " + xpath); + } + + void SearchImpl::callService(const ::std::string & fn, const SearchServicePtr & s, SearchHits & sh) const + { + auto fmt = AdHoc::Buffer::getFormat(s->baseurl); + auto url = (*fmt % fn).str(); + auto doc = getDoc(url, + HTML_PARSE_RECOVER | HTML_PARSE_NODEFDTD | HTML_PARSE_NOIMPLIED | + HTML_PARSE_NOWARNING | HTML_PARSE_NOERROR); + auto xpathCtx = getXPathCxt(doc); + auto xpathObj = getXPathObj(s->listxpath, xpathCtx, xmlXPathObjectType::XPATH_NODESET); + log->messagebf(LOG::INFO, "%d nodes matched %s", xpathObj->nodesetval->nodeNr, s->listxpath); + for (int row = 0; row < xpathObj->nodesetval->nodeNr; row += 1) { + xpathCtx->node = xpathObj->nodesetval->nodeTab[row]; + auto xpathObjI = getXPathObj(s->urlxpath, xpathCtx, xmlXPathObjectType::XPATH_STRING); + if (xpathObjI->stringval && *xpathObjI->stringval) { + sh.push_back(new SearchHit(0, s->id, (const char *) xpathObjI->stringval)); + } + } + } + + SearchHits SearchImpl::getMatches(const ::std::string & fn, const ::Ice::Current & c) + { + SearchHits sh; + for (const auto & s : getServices(c)) { + callService(fn, s, sh); + } + return sh; + } + + ::IceUtil::Optional<::std::string> SearchImpl::feelingLucky(const ::std::string & fn, const ::Ice::Current & c) + { + const auto ms = getMatches(fn, c); + if (ms.empty()) + return IceUtil::None; + return ms.front()->url; + } +} + diff --git a/service/apiImpl.h b/service/apiImpl.h new file mode 100644 index 0000000..e56368c --- /dev/null +++ b/service/apiImpl.h @@ -0,0 +1,25 @@ +#ifndef MIRRORSEARCH_APIIMPL_H +#define MIRRORSEARCH_APIIMPL_H + +#include +#include +#include + +namespace MirrorSearch { + class SearchImpl : public Search, public IceTray::AbstractDatabaseClient { + public: + SearchImpl(IceTray::DatabasePoolPtr); + + virtual SearchServices getServices(const ::Ice::Current& = ::Ice::Current()) override; + virtual SearchHits getMatches(const ::std::string&, const ::Ice::Current& = ::Ice::Current()) override; + virtual ::IceUtil::Optional<::std::string> feelingLucky(const ::std::string&, const ::Ice::Current& = ::Ice::Current()) override; + + private: + void callService(const ::std::string & fn, const SearchServicePtr & s, SearchHits & sh) const; + + LOG::LoggerPtr log; + }; +} + +#endif + diff --git a/service/data.sql b/service/data.sql new file mode 100644 index 0000000..e14e721 --- /dev/null +++ b/service/data.sql @@ -0,0 +1,3 @@ +INSERT INTO searchservices(name, baseurl, listxpath, urlxpath) + VALUES('file searching mock', 'file://$SCRIPTDIR/fixtures/filesearching/%s.html', '//pre[@class=''list'']/a[@class=''lf'']', 'string(@href)') + ; diff --git a/service/fixtures/filesearching/xtrans-1.3.5.tar.bz2.html b/service/fixtures/filesearching/xtrans-1.3.5.tar.bz2.html new file mode 100644 index 0000000..27b627a --- /dev/null +++ b/service/fixtures/filesearching/xtrans-1.3.5.tar.bz2.html @@ -0,0 +1,251 @@ + + +FileSearch - xtrans-1.3.5.tar.bz2 + + + +
+ + + + + + + +
+ + + + + + +
+ + + + + + + + + + + + +

search

advanced search
...
+ + + + + + + + + + +
  +   + +   + + +
+
+
+ + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + +
+   1   182.5K ftp.cs.mun.ca/pub/mirror/gentoo/distfiles/xtrans-1.3.5.tar.bz2
+   2   182.5K ftp.dvo.ru/pub/Gentoo/distfiles/xtrans-1.3.5.tar.bz2
+   3   182.5K ftp.dvo.ru/pub/distfiles/xtrans-1.3.5.tar.bz2
+   4   182.5K ftp.fu-berlin.de/unix/X11/FTP.X.ORG/pub/individual/lib/xtrans-1.3.5.tar.bz2
+   5      536 ftp.fu-berlin.de/unix/X11/FTP.X.ORG/pub/individual/lib/xtrans-1.3.5.tar.bz2.sig
+   6   182.5K ftp.gr.debian.org/pub/X11/X.org/individual/lib/xtrans-1.3.5.tar.bz2
+   7      536 ftp.gr.debian.org/pub/X11/X.org/individual/lib/xtrans-1.3.5.tar.bz2.sig
+   8   182.5K ftp.linux.org.tr/gentoo/distfiles/xtrans-1.3.5.tar.bz2
+   9   182.5K ftp.ntua.gr/pub/X11/X.org/individual/lib/xtrans-1.3.5.tar.bz2
+  10      536 ftp.ntua.gr/pub/X11/X.org/individual/lib/xtrans-1.3.5.tar.bz2.sig
+  11   182.5K ftp.rediris.es/sites/gentoo.org/distfiles/xtrans-1.3.5.tar.bz2
+  12   182.5K ftp.tr.debian.org/gentoo/distfiles/xtrans-1.3.5.tar.bz2
+
+ + + + + +
+ + + + +
+ + +
+

+
+ + + + + + + diff --git a/service/fixtures/filesearching/zstd-1.3.3.tar.gz.html b/service/fixtures/filesearching/zstd-1.3.3.tar.gz.html new file mode 100644 index 0000000..74e70e2 --- /dev/null +++ b/service/fixtures/filesearching/zstd-1.3.3.tar.gz.html @@ -0,0 +1,243 @@ + + +FileSearch - zstd-1.3.3.tar.gz + + + +
+ + + + + + + +
+ + + + + + +
+ + + + + + + + + + + + +

search

advanced search
...
+ + + + + + + + + + +
  +   + +   + + +
+
+
+ + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + +
+
+ + + + + +
+zstd-1.3.3.tar.gz not found
+Try to use search forms to refine the parameters of your request. +
+ + + + + +
+ + +
+

+
+ + + + + + + diff --git a/service/main.cpp b/service/main.cpp new file mode 100644 index 0000000..97bc89e --- /dev/null +++ b/service/main.cpp @@ -0,0 +1,18 @@ +#include +#include +#include +#include "apiImpl.h" + +namespace MirrorSearch { + class Api : public IceTray::Service { + public: + void addObjects(const std::string &, const Ice::CommunicatorPtr & ic, const Ice::StringSeq &, const Ice::ObjectAdapterPtr & adp) override + { + auto dbpool = getConnectionPool(ic, "postgresql", "MirrorSearch"); + adp->add(new SearchImpl(dbpool), ic->stringToIdentity("Search")); + } + }; + + NAMEDFACTORY("default", MirrorSearch::Api, IceTray::ServiceFactory); +} + diff --git a/service/models.ice b/service/models.ice new file mode 100644 index 0000000..cb48459 --- /dev/null +++ b/service/models.ice @@ -0,0 +1,25 @@ +#ifndef MIRRORSEARCH_MODELS +#define MIRRORSEARCH_MODELS + +module MirrorSearch { + class SearchService { + ["slicer:db:pkey"] + int id; + string name; + string baseurl; + string listxpath; + string urlxpath; + }; + class SearchHit { + ["slicer:db:pkey"] + int id; + int serviceid; + string url; + }; + + sequence SearchServices; + sequence SearchHits; +}; + +#endif + diff --git a/service/schema.sql b/service/schema.sql new file mode 100644 index 0000000..b529172 --- /dev/null +++ b/service/schema.sql @@ -0,0 +1,8 @@ +CREATE TABLE searchservices( + id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name text not null, + baseurl text not null, + listxpath text not null, + urlxpath text not null +); + diff --git a/service/sql/getServices.sql b/service/sql/getServices.sql new file mode 100644 index 0000000..40382f2 --- /dev/null +++ b/service/sql/getServices.sql @@ -0,0 +1,3 @@ +SELECT id, name, baseurl, listxpath, urlxpath +FROM searchservices +ORDER BY id diff --git a/service/test.cpp b/service/test.cpp new file mode 100644 index 0000000..c115712 --- /dev/null +++ b/service/test.cpp @@ -0,0 +1,77 @@ +#define BOOST_TEST_MODULE MirrorSearch +#include + +#include +#include +#include +#include + +class Service : PQ::Mock, public IceTray::DryIce { + public: + Service() : PQ::Mock("user=postgres", "MirrorSearch", { + rootDir / "schema.sql", + rootDir / "data.sql" + }) { } + +}; + +class TestClient : public IceTray::DryIceClient { + public: + TestClient() : + s(getProxy("Search")) + { + } + + MirrorSearch::SearchPrx s; +}; + +BOOST_TEST_GLOBAL_FIXTURE(Service); + +BOOST_FIXTURE_TEST_SUITE(tc, TestClient); + +BOOST_AUTO_TEST_CASE(sanity) +{ + BOOST_REQUIRE(s); + s->ice_ping(); +} + +BOOST_AUTO_TEST_CASE(getServices) +{ + auto ss = s->getServices(); + BOOST_REQUIRE_EQUAL(ss.size(), 1); + BOOST_CHECK_EQUAL(ss.front()->id, 1); + BOOST_CHECK_EQUAL(ss.front()->name, "file searching mock"); + BOOST_CHECK_NE(ss.front()->baseurl, "file://$SCRIPTDIR/fixtures/filesearching/%s.html"); + BOOST_CHECK_EQUAL(ss.front()->baseurl.substr(0, 8), "file:///"); + BOOST_CHECK(!ss.front()->listxpath.empty()); + BOOST_CHECK(!ss.front()->urlxpath.empty()); +} + +BOOST_AUTO_TEST_CASE(getMatches_zstd_notfound) +{ + auto ms = s->getMatches("zstd-1.3.3.tar.gz"); + BOOST_REQUIRE(ms.empty()); +} + +BOOST_AUTO_TEST_CASE(getMatches_xtrans) +{ + auto ms = s->getMatches("xtrans-1.3.5.tar.bz2"); + BOOST_REQUIRE_EQUAL(ms.size(), 12); + BOOST_REQUIRE_EQUAL(ms.front()->url, "ftp://ftp.cs.mun.ca/pub/mirror/gentoo/distfiles/xtrans-1.3.5.tar.bz2"); + BOOST_REQUIRE_EQUAL(ms.back()->url, "ftp://ftp.tr.debian.org/gentoo/distfiles/xtrans-1.3.5.tar.bz2"); +} + +BOOST_AUTO_TEST_CASE(getMatches_zstd_notfound_lucky) +{ + BOOST_REQUIRE(!s->feelingLucky("zstd-1.3.3.tar.gz")); +} + +BOOST_AUTO_TEST_CASE(getMatches_xtrans_lucky) +{ + auto fl = s->feelingLucky("xtrans-1.3.5.tar.bz2"); + BOOST_REQUIRE(fl); + BOOST_REQUIRE_EQUAL(*fl, "ftp://ftp.cs.mun.ca/pub/mirror/gentoo/distfiles/xtrans-1.3.5.tar.bz2"); +} + +BOOST_AUTO_TEST_SUITE_END(); + -- cgit v1.2.3