aboutsummaryrefslogtreecommitdiff
path: root/mod
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2018-09-05 21:23:41 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2018-09-08 17:44:57 +0300
commit70c1cdfd8f34472761fe5ec97f0713990c1b4f5b (patch)
treef2e631c10563bcc0cde07e4359c11b800a188d86 /mod
parent3be834183ae36c321e4b560dce9a63cee846e63d (diff)
Add multi-tenancy support
Diffstat (limited to 'mod')
-rw-r--r--mod/build-config.cxx9
-rw-r--r--mod/mod-build-force.cxx3
-rw-r--r--mod/mod-build-log.cxx18
-rw-r--r--mod/mod-build-result.cxx23
-rw-r--r--mod/mod-build-task.cxx69
-rw-r--r--mod/mod-builds.cxx119
-rw-r--r--mod/mod-ci.cxx2
-rw-r--r--mod/mod-package-details.cxx32
-rw-r--r--mod/mod-package-search.cxx20
-rw-r--r--mod/mod-package-version-details.cxx17
-rw-r--r--mod/mod-repository-details.cxx13
-rw-r--r--mod/mod-repository-root.cxx18
-rw-r--r--mod/mod-submit.cxx2
-rw-r--r--mod/module.hxx10
-rw-r--r--mod/page.cxx31
-rw-r--r--mod/page.hxx59
-rw-r--r--mod/utility.hxx25
17 files changed, 312 insertions, 158 deletions
diff --git a/mod/build-config.cxx b/mod/build-config.cxx
index 4913555..e838a59 100644
--- a/mod/build-config.cxx
+++ b/mod/build-config.cxx
@@ -14,6 +14,8 @@
#include <web/mime-url-encoding.hxx>
+#include <mod/utility.hxx>
+
namespace brep
{
using namespace std;
@@ -117,7 +119,7 @@ namespace brep
// needs to be url-encoded, and only in the query part of the URL. We embed
// the package version into the URL path part and so don't encode it.
//
- string url (host + root.representation () +
+ string url (host + tenant_dir (root, b.tenant).representation () +
mime_url_encode (b.package_name.string (), false) + '/' +
b.package_version.string () + "/log/" +
mime_url_encode (b.configuration, false) + '/' +
@@ -140,7 +142,7 @@ namespace brep
// we embed the package version into the URL query part, where it is not
// encoded by design.
//
- return host + root.string () +
+ return host + tenant_dir (root, b.tenant).string () +
"?build-force&pn=" + mime_url_encode (b.package_name.string ()) +
"&pv=" + b.package_version.string () +
"&cf=" + mime_url_encode (b.configuration) +
@@ -148,7 +150,8 @@ namespace brep
}
bool
- match (const string& config_pattern, const optional<string>& target_pattern,
+ match (const string& config_pattern,
+ const optional<string>& target_pattern,
const build_config& c)
{
return path_match (config_pattern, c.name) &&
diff --git a/mod/mod-build-force.cxx b/mod/mod-build-force.cxx
index b6514ce..ddc1301 100644
--- a/mod/mod-build-force.cxx
+++ b/mod/mod-build-force.cxx
@@ -122,7 +122,7 @@ handle (request& rq, response& rs)
if (c.empty ())
throw invalid_argument ("no configuration name");
- id = build_id (package_id (move (p), package_version),
+ id = build_id (package_id (move (tenant), move (p), package_version),
move (c),
toolchain_version);
}
@@ -168,6 +168,7 @@ handle (request& rq, response& rs)
build_db_->update (b);
l1 ([&]{trace << "force rebuild for "
+ << b->tenant << ' '
<< b->package_name << '/' << b->package_version << ' '
<< b->configuration << ' '
<< b->toolchain_name << '-' << b->toolchain_version
diff --git a/mod/mod-build-log.cxx b/mod/mod-build-log.cxx
index 57135b6..70e2c7e 100644
--- a/mod/mod-build-log.cxx
+++ b/mod/mod-build-log.cxx
@@ -78,13 +78,22 @@ handle (request& rq, response& rs)
path lpath (rq.path ().leaf (options_->root ()));
+ // If the tenant is not empty then it is contained in the leftmost path
+ // component (see repository_root for details). Strip it if that's the case.
+ //
+ if (!tenant.empty ())
+ {
+ assert (!lpath.empty ());
+ lpath = path (++lpath.begin (), lpath.end ());
+ }
+
+ assert (!lpath.empty ());
+
try
{
auto i (lpath.begin ());
- assert (i != lpath.end ());
package_name name;
-
try
{
name = package_name (*i++);
@@ -127,7 +136,7 @@ handle (request& rq, response& rs)
version toolchain_version (parse_version (*i++, "toolchain version"));
- id = build_id (package_id (move (name), package_version),
+ id = build_id (package_id (tenant, move (name), package_version),
move (config),
toolchain_version);
@@ -164,6 +173,7 @@ handle (request& rq, response& rs)
auto config_expired = [&trace, &lpath, this] (const string& d)
{
l2 ([&]{trace << "package build configuration for " << lpath
+ << (!tenant.empty () ? "(" + tenant + ")" : "")
<< " expired: " << d;});
throw invalid_request (404, "package build configuration expired: " + d);
@@ -204,6 +214,8 @@ handle (request& rq, response& rs)
auto print_header = [&os, &b] ()
{
+ // @@ Should we print the tenant? How to call it if that's the case?
+ //
os << "package: " << b->package_name << endl
<< "version: " << b->package_version << endl
<< "toolchain: " << b->toolchain_name << '-' << b->toolchain_version
diff --git a/mod/mod-build-result.cxx b/mod/mod-build-result.cxx
index 65e8425..1ba1bec 100644
--- a/mod/mod-build-result.cxx
+++ b/mod/mod-build-result.cxx
@@ -106,8 +106,8 @@ handle (request& rq, response&)
}
// Parse the task response session to obtain the build configuration name and
- // the timestamp, and to make sure the session matches the result manifest's
- // package name and version.
+ // the timestamp, and to make sure the session matches tenant and the result
+ // manifest's package name, and version.
//
build_id id;
timestamp session_timestamp;
@@ -116,9 +116,18 @@ handle (request& rq, response&)
{
const string& s (rqm.session);
- size_t p (s.find ('/')); // End of package name.
+ size_t p (s.find ('/')); // End of tenant.
- if (p == 0)
+ if (p == string::npos)
+ throw invalid_argument ("no package name");
+
+ if (tenant.compare (0, tenant.size (), s, 0, p) != 0)
+ throw invalid_argument ("tenant mismatch");
+
+ size_t b (p + 1); // Start of package name.
+ p = s.find ('/', b); // End of package name.
+
+ if (p == b)
throw invalid_argument ("empty package name");
if (p == string::npos)
@@ -127,11 +136,11 @@ handle (request& rq, response&)
package_name& name (rqm.result.name);
{
const string& n (name.string ());
- if (n.compare (0, n.size (), s, 0, p) != 0)
+ if (n.compare (0, n.size (), s, b, p - b) != 0)
throw invalid_argument ("package name mismatch");
}
- size_t b (p + 1); // Start of version.
+ b = p + 1; // Start of version.
p = s.find ('/', b); // End of version.
if (p == string::npos)
@@ -172,7 +181,7 @@ handle (request& rq, response&)
version toolchain_version (parse_version ("toolchain version"));
- id = build_id (package_id (move (name), package_version),
+ id = build_id (package_id (move (tenant), move (name), package_version),
move (config),
toolchain_version);
diff --git a/mod/mod-build-task.cxx b/mod/mod-build-task.cxx
index 4e56d02..77652ce 100644
--- a/mod/mod-build-task.cxx
+++ b/mod/mod-build-task.cxx
@@ -188,13 +188,15 @@ handle (request& rq, response& rs)
chrono::duration_cast<std::chrono::nanoseconds> (
b->timestamp.time_since_epoch ()).count ());
- string session (b->package_name.string () + '/' +
- b->package_version.string () +
- '/' + b->configuration +
- '/' + b->toolchain_version.string () +
- '/' + to_string (ts));
-
- string result_url (options_->host () + options_->root ().string () +
+ string session (b->tenant + '/' +
+ b->package_name.string () + '/' +
+ b->package_version.string () + '/' +
+ b->configuration + '/' +
+ b->toolchain_version.string () + '/' +
+ to_string (ts));
+
+ string result_url (options_->host () +
+ tenant_dir (options_->root (), b->tenant).string () +
"?build-result");
lazy_shared_ptr<build_repository> r (p->internal_repository);
@@ -319,6 +321,10 @@ handle (request& rq, response& rs)
// harmful in that: updates are infrequent and missed packages will be
// picked up on the next request.
//
+ // Also note that we disregard the request tenant and operate on the whole
+ // set of the packages and builds. In future we may add support for
+ // building packages for a specific tenant.
+ //
using pkg_query = query<buildable_package>;
using prep_pkg_query = prepared_query<buildable_package>;
@@ -330,18 +336,17 @@ handle (request& rq, response& rs)
if (!rp.empty ())
pq = pq &&
- pkg_query::build_repository::name.in_range (rp.begin (), rp.end ());
+ pkg_query::build_repository::id.canonical_name.in_range (rp.begin (),
+ rp.end ());
// Specify the portion.
//
size_t offset (0);
pq += "ORDER BY" +
- pkg_query::build_package::id.name + "," +
- pkg_query::build_package::id.version.epoch + "," +
- pkg_query::build_package::id.version.canonical_upstream + "," +
- pkg_query::build_package::id.version.canonical_release + "," +
- pkg_query::build_package::id.version.revision +
+ pkg_query::build_package::id.tenant + "," +
+ pkg_query::build_package::id.name +
+ order_by_version (pkg_query::build_package::id.version, false) +
"OFFSET" + pkg_query::_ref (offset) + "LIMIT 50";
connection_ptr conn (build_db_->connection ());
@@ -368,20 +373,23 @@ handle (request& rq, response& rs)
const auto& qv (bld_query::id.package.version);
bld_query bq (
- bld_query::id.package.name == bld_query::_ref (id.name) &&
+ bld_query::id.package.tenant == bld_query::_ref (id.tenant) &&
+
+ bld_query::id.package.name == bld_query::_ref (id.name) &&
- qv.epoch == bld_query::_ref (id.version.epoch) &&
+ qv.epoch == bld_query::_ref (id.version.epoch) &&
qv.canonical_upstream ==
- bld_query::_ref (id.version.canonical_upstream) &&
- qv.canonical_release == bld_query::_ref (id.version.canonical_release) &&
- qv.revision == bld_query::_ref (id.version.revision) &&
+ bld_query::_ref (id.version.canonical_upstream) &&
+ qv.canonical_release ==
+ bld_query::_ref (id.version.canonical_release) &&
+ qv.revision == bld_query::_ref (id.version.revision) &&
bld_query::id.configuration.in_range (cfg_names.begin (),
- cfg_names.end ()) &&
+ cfg_names.end ()) &&
compare_version_eq (bld_query::id.toolchain_version,
toolchain_version,
- true) &&
+ true) &&
(bld_query::state == "built" ||
((bld_query::force == "forcing" &&
@@ -467,13 +475,14 @@ handle (request& rq, response& rs)
shared_ptr<build> b (build_db_->find<build> (bid));
optional<string> cl (challenge ());
- // If build configuration doesn't exist then create the new one and
- // persist. Otherwise put it into the building state, refresh the
- // timestamp and update.
+ // If build configuration doesn't exist then create the new one
+ // and persist. Otherwise put it into the building state, refresh
+ // the timestamp and update.
//
if (b == nullptr)
{
- b = make_shared<build> (move (bid.package.name),
+ b = make_shared<build> (move (bid.package.tenant),
+ move (bid.package.name),
move (bp.version),
move (bid.configuration),
move (tqm.toolchain_name),
@@ -493,9 +502,9 @@ handle (request& rq, response& rs)
//
// Note that in both cases we keep the status intact to be able
// to compare it with the final one in the result request
- // handling in order to decide if to send the notification email.
- // The same is true for the forced flag (in the sense that we
- // don't set the force state to unforced).
+ // handling in order to decide if to send the notification
+ // email. The same is true for the forced flag (in the sense
+ // that we don't set the force state to unforced).
//
// Load the section to assert the above statement.
//
@@ -558,8 +567,7 @@ handle (request& rq, response& rs)
// 2: overall status
// 3: timestamp (less is preferred)
//
- auto cmp = [] (const shared_ptr<build>& x,
- const shared_ptr<build>& y) -> bool
+ auto cmp = [] (const shared_ptr<build>& x, const shared_ptr<build>& y)
{
if (x->force != y->force)
return x->force > y->force; // Forced goes first.
@@ -611,7 +619,8 @@ handle (request& rq, response& rs)
shared_ptr<build_package> p (
build_db_->find<build_package> (b->id.package));
- if (p != nullptr && p->internal_repository != nullptr &&
+ if (p != nullptr &&
+ p->internal_repository != nullptr &&
!exclude (*p, *cm.config))
{
assert (b->status);
diff --git a/mod/mod-builds.cxx b/mod/mod-builds.cxx
index 19d187c..d2e3a25 100644
--- a/mod/mod-builds.cxx
+++ b/mod/mod-builds.cxx
@@ -92,7 +92,9 @@ transform (const string& s)
template <typename T>
static inline query<T>
-build_query (const brep::cstrings& configs, const brep::params::builds& params)
+build_query (const brep::cstrings& configs,
+ const brep::params::builds& params,
+ const brep::optional<string>& tenant)
{
using namespace brep;
using query = query<T>;
@@ -102,6 +104,11 @@ build_query (const brep::cstrings& configs, const brep::params::builds& params)
? qb::id.configuration.in_range (configs.begin (), configs.end ())
: query (true));
+ const auto& pid (qb::id.package);
+
+ if (tenant)
+ q = q && pid.tenant == *tenant;
+
// Note that there is no error reported if the filter parameters parsing
// fails. Instead, it is considered that no package builds match such a
// query.
@@ -111,13 +118,13 @@ build_query (const brep::cstrings& configs, const brep::params::builds& params)
// Package name.
//
if (!params.name ().empty ())
- q = q && qb::id.package.name.like (
- package_name (transform (params.name ()), package_name::raw_string));
+ q = q && pid.name.like (package_name (transform (params.name ()),
+ package_name::raw_string));
// Package version.
//
if (!params.version ().empty () && params.version () != "*")
- q = q && compare_version_eq (qb::id.package.version,
+ q = q && compare_version_eq (pid.version,
version (params.version ()), // May throw.
true);
@@ -200,12 +207,12 @@ build_query (const brep::cstrings& configs, const brep::params::builds& params)
template <typename T, typename P = typename query<T>::build_package>
static inline query<T>
-package_query (const brep::params::builds& params)
+package_query (const brep::params::builds& params, const string& tenant)
{
using namespace brep;
using query = query<T>;
- query q (true);
+ query q (P::id.tenant == tenant);
// Note that there is no error reported if the filter parameters parsing
// fails. Instead, it is considered that no packages match such a query.
@@ -240,22 +247,24 @@ package_id_eq (const ID& x, const brep::package_id& y)
using query = query<T>;
const auto& qv (x.version);
- return x.name == query::_ref (y.name) &&
- qv.epoch == query::_ref (y.version.epoch) &&
+ return
+ x.tenant == query::_ref (y.tenant) &&
+ x.name == query::_ref (y.name) &&
+ qv.epoch == query::_ref (y.version.epoch) &&
qv.canonical_upstream == query::_ref (y.version.canonical_upstream) &&
- qv.canonical_release == query::_ref (y.version.canonical_release) &&
+ qv.canonical_release == query::_ref (y.version.canonical_release) &&
qv.revision == query::_ref (y.version.revision);
}
static const vector<pair<string, string>> build_results ({
- {"unbuilt", "<unbuilt>"},
- {"*", "*"},
- {"pending", "pending"},
+ {"unbuilt", "<unbuilt>"},
+ {"*", "*"},
+ {"pending", "pending"},
{"building", "building"},
- {"success", "success"},
- {"warning", "warning"},
- {"error", "error"},
- {"abort", "abort"},
+ {"success", "success"},
+ {"warning", "warning"},
+ {"error", "error"},
+ {"abort", "abort"},
{"abnormal", "abnormal"}});
bool brep::builds::
@@ -309,7 +318,7 @@ handle (request& rq, response& rs)
<< SCRIPT << " " << ~SCRIPT
<< ~HEAD
<< BODY
- << DIV_HEADER (root, options_->logo (), options_->menu ())
+ << DIV_HEADER (options_->logo (), options_->menu (), root, tenant)
<< DIV(ID="content");
// Return the list of distinct toolchain name/version pairs. The build db
@@ -323,6 +332,7 @@ handle (request& rq, response& rs)
toolchains r;
for (auto& t: build_db_->query<toolchain> (
+ (query::id.package.tenant == tenant) +
"ORDER BY" + query::toolchain_name +
order_by_version_desc (query::id.toolchain_version, false)))
r.emplace_back (move (t.name), move (t.version));
@@ -356,11 +366,11 @@ handle (request& rq, response& rs)
}
// The 'action' attribute is optional in HTML5. While the standard
- // doesn't specify browser behavior explicitly for the case the attribute
- // is omitted, the only reasonable behavior is to default it to the
- // current document URL. Note that we specify the function name using the
- // "hidden" <input/> element since the action url must not contain the
- // query part.
+ // doesn't specify browser behavior explicitly for the case the
+ // attribute is omitted, the only reasonable behavior is to default it
+ // to the current document URL. Note that we specify the function name
+ // using the "hidden" <input/> element since the action url must not
+ // contain the query part.
//
s << FORM
<< TABLE(ID="filter", CLASS="proplist")
@@ -418,7 +428,7 @@ handle (request& rq, response& rs)
transaction t (build_db_->begin ());
count = build_db_->query_value<package_build_count> (
- build_query<package_build_count> (*build_conf_names_, params));
+ build_query<package_build_count> (*build_conf_names_, params, tenant));
// Print the filter form.
//
@@ -433,8 +443,8 @@ handle (request& rq, response& rs)
//
s << DIV;
for (auto& pb: build_db_->query<package_build> (
- build_query<package_build> (*build_conf_names_, params) +
- "ORDER BY" + query<build>::timestamp + "DESC" +
+ build_query<package_build> (*build_conf_names_, params, tenant) +
+ "ORDER BY" + query<package_build>::build::timestamp + "DESC" +
"OFFSET" + to_string (page * page_configs) +
"LIMIT" + to_string (page_configs)))
{
@@ -451,8 +461,8 @@ handle (request& rq, response& rs)
s << TABLE(CLASS="proplist build")
<< TBODY
- << TR_NAME (b.package_name, string (), root)
- << TR_VERSION (b.package_name, b.package_version, root)
+ << TR_NAME (b.package_name, string (), root, tenant)
+ << TR_VERSION (b.package_name, b.package_version, root, tenant)
<< TR_VALUE ("toolchain",
b.toolchain_name + '-' +
b.toolchain_version.string ())
@@ -470,8 +480,8 @@ handle (request& rq, response& rs)
}
else // Print unbuilt package configurations.
{
- // Parameters to use for package build configurations queries. Note that we
- // cleanup the machine and the result filter arguments, as they are
+ // Parameters to use for package build configurations queries. Note that
+ // we cleanup the machine and the result filter arguments, as they are
// irrelevant for unbuilt configurations.
//
params::builds bld_params (params);
@@ -506,8 +516,8 @@ handle (request& rq, response& rs)
}
};
- // Note that config_toolchains contains shallow references to the toolchain
- // names and versions.
+ // Note that config_toolchains contains shallow references to the
+ // toolchain names and versions.
//
set<config_toolchain> config_toolchains;
{
@@ -576,12 +586,15 @@ handle (request& rq, response& rs)
// due to the build configuration target change. We should deduct such
// builds count from the number of existing package builds.
//
- size_t nmax (config_toolchains.size () *
- build_db_->query_value<buildable_package_count> (
- package_query<buildable_package_count> (params)));
+ size_t nmax (
+ config_toolchains.size () *
+ build_db_->query_value<buildable_package_count> (
+ package_query<buildable_package_count> (params, tenant)));
size_t ncur = build_db_->query_value<package_build_count> (
- build_query<package_build_count> (*build_conf_names_, bld_params));
+ build_query<package_build_count> (*build_conf_names_,
+ bld_params,
+ tenant));
// From now we will be using specific package name and version for each
// build database query.
@@ -602,11 +615,18 @@ handle (request& rq, response& rs)
package_id id;
string config;
+ const auto& bid (bld_query::build::id);
+
bld_query bq (
- package_id_eq<package_build_count> (
- bld_query::build::id.package, id) &&
- bld_query::build::id.configuration == bld_query::_ref (config) &&
- build_query<package_build_count> (cstrings (), bld_params));
+ package_id_eq<package_build_count> (bid.package, id) &&
+ bid.configuration == bld_query::_ref (config) &&
+
+ // Note that the query already constrains the tenant via the build
+ // package id.
+ //
+ build_query<package_build_count> (cstrings () /* configs */,
+ bld_params,
+ nullopt /* tenant */));
prep_bld_query bld_prep_query (
build_db_->prepare_query<package_build_count> (
@@ -620,7 +640,8 @@ handle (request& rq, response& rs)
// form parameters.
//
using query = query<build_constrained_package>;
- query q (package_query<build_constrained_package, query> (params));
+ query q (package_query<build_constrained_package, query> (params,
+ tenant));
for (const auto& p: build_db_->query<build_constrained_package> (q))
{
@@ -675,13 +696,15 @@ handle (request& rq, response& rs)
using pkg_query = query<buildable_package>;
using prep_pkg_query = prepared_query<buildable_package>;
- pkg_query pq (package_query<buildable_package> (params));
+ pkg_query pq (package_query<buildable_package> (params, tenant));
// Specify the portion. Note that we will still be querying packages in
// chunks, not to hold locks for too long.
//
size_t offset (0);
+ // @@ TENANT: use tenant for sorting when add support for global view.
+ //
pq += "ORDER BY" +
pkg_query::build_package::id.name +
order_by_version_desc (pkg_query::build_package::id.version, false) +
@@ -706,7 +729,13 @@ handle (request& rq, response& rs)
bld_query bq (
package_id_eq<package_build> (bld_query::build::id.package, id) &&
- build_query<package_build> (*build_conf_names_, bld_params));
+
+ // Note that the query already constrains the tenant via the build
+ // package id.
+ //
+ build_query<package_build> (*build_conf_names_,
+ bld_params,
+ nullopt /* tenant */));
prep_bld_query bld_prep_query (
conn->prepare_query<package_build> ("mod-builds-build-query", bq));
@@ -805,8 +834,8 @@ handle (request& rq, response& rs)
s << TABLE(CLASS="proplist build")
<< TBODY
- << TR_NAME (id.name, string (), root)
- << TR_VERSION (id.name, p.version, root)
+ << TR_NAME (id.name, string (), root, tenant)
+ << TR_VERSION (id.name, p.version, root, tenant)
<< TR_VALUE ("toolchain",
string (ct.toolchain_name) + '-' +
ct.toolchain_version.string ())
@@ -829,7 +858,7 @@ handle (request& rq, response& rs)
s << ~DIV;
}
- string u (root.string () + "?builds");
+ string u (tenant_dir (root, tenant).string () + "?builds");
if (!params.name ().empty ())
{
diff --git a/mod/mod-ci.cxx b/mod/mod-ci.cxx
index 79472d0..e16a1a1 100644
--- a/mod/mod-ci.cxx
+++ b/mod/mod-ci.cxx
@@ -182,7 +182,7 @@ handle (request& rq, response& rs)
<< CSS_LINKS (path ("ci.css"), root)
<< ~HEAD
<< BODY
- << DIV_HEADER (root, options_->logo (), options_->menu ())
+ << DIV_HEADER (options_->logo (), options_->menu (), root, tenant)
<< DIV(ID="content") << *form_ << ~DIV
<< ~BODY
<< ~HTML;
diff --git a/mod/mod-package-details.cxx b/mod/mod-package-details.cxx
index 3db9e1f..6ec0b0f 100644
--- a/mod/mod-package-details.cxx
+++ b/mod/mod-package-details.cxx
@@ -50,7 +50,9 @@ init (scanner& s)
template <typename T>
static inline query<T>
-search_params (const brep::package_name& n, const brep::string& q)
+search_params (const brep::string& q,
+ const brep::string& t,
+ const brep::package_name& n)
{
using query = query<T>;
@@ -59,6 +61,8 @@ search_params (const brep::package_name& n, const brep::string& q)
? query ("NULL")
: "plainto_tsquery (" + query::_val (q) + ")") +
"," +
+ query::_val (t) +
+ "," +
query::_val (n) +
")";
}
@@ -100,12 +104,13 @@ handle (request& rq, response& rs)
try
{
+ using query = query<latest_package>;
+
package_name n (*rq.path ().rbegin ());
latest_package lp;
if (!package_db_->query_one<latest_package> (
- query<latest_package> ("(" + query<latest_package>::_val (n) + ")"),
- lp))
+ "(" + query::_val (tenant) + "," + query::_val (n) + ")", lp))
throw invalid_request (404, "Package '" + n.string () + "' not found");
pkg = package_db_->load<package> (lp.id);
@@ -115,7 +120,7 @@ handle (request& rq, response& rs)
throw invalid_request (400, "invalid package name format");
}
- const package_name& name (pkg->id.name);
+ const package_name& name (pkg->name);
const string ename (mime_url_encode (name.string (), false));
auto url = [&ename] (bool f = false,
@@ -156,7 +161,7 @@ handle (request& rq, response& rs)
<< SCRIPT << " " << ~SCRIPT
<< ~HEAD
<< BODY
- << DIV_HEADER (root, options_->logo (), options_->menu ())
+ << DIV_HEADER (options_->logo (), options_->menu (), root, tenant)
<< DIV(ID="content");
if (full)
@@ -187,7 +192,7 @@ handle (request& rq, response& rs)
s << TABLE(CLASS="proplist", ID="package")
<< TBODY
<< TR_LICENSE (licenses)
- << TR_PROJECT (pkg->project, root);
+ << TR_PROJECT (pkg->project, root, tenant);
if (pkg->url)
s << TR_URL (*pkg->url);
@@ -201,14 +206,14 @@ handle (request& rq, response& rs)
if (pkg->email)
s << TR_EMAIL (*pkg->email);
- s << TR_TAGS (pkg->tags, root)
+ s << TR_TAGS (pkg->tags, root, tenant)
<< ~TBODY
<< ~TABLE;
}
auto pkg_count (
package_db_->query_value<package_count> (
- search_params<package_count> (name, squery)));
+ search_params<package_count> (squery, tenant, name)));
s << FORM_SEARCH (squery)
<< DIV_COUNTER (pkg_count, "Version", "Versions");
@@ -218,7 +223,7 @@ handle (request& rq, response& rs)
s << DIV;
for (const auto& pr:
package_db_->query<package_search_rank> (
- search_params<package_search_rank> (name, squery) +
+ search_params<package_search_rank> (squery, tenant, name) +
"ORDER BY rank DESC, version_epoch DESC, "
"version_canonical_upstream DESC, version_canonical_release DESC, "
"version_revision DESC" +
@@ -229,7 +234,7 @@ handle (request& rq, response& rs)
s << TABLE(CLASS="proplist version")
<< TBODY
- << TR_VERSION (name, p->version, root)
+ << TR_VERSION (name, p->version, root, tenant)
// @@ Shouldn't we skip low priority row ? Don't think so, why?
//
@@ -256,8 +261,11 @@ handle (request& rq, response& rs)
//
// Hm, I am not so sure about this. Consider: stable/testing/unstable.
//
- s << TR_REPOSITORY (p->internal_repository.object_id (), root)
- << TR_DEPENDS (p->dependencies, root)
+ s << TR_REPOSITORY (
+ p->internal_repository.object_id ().canonical_name,
+ root,
+ tenant)
+ << TR_DEPENDS (p->dependencies, root, tenant)
<< TR_REQUIRES (p->requirements)
<< ~TBODY
<< ~TABLE;
diff --git a/mod/mod-package-search.cxx b/mod/mod-package-search.cxx
index a86e5a7..347abf1 100644
--- a/mod/mod-package-search.cxx
+++ b/mod/mod-package-search.cxx
@@ -67,13 +67,15 @@ init (scanner& s)
template <typename T>
static inline query<T>
-search_param (const brep::string& q)
+search_param (const brep::string& q, const brep::string& t)
{
using query = query<T>;
return "(" +
(q.empty ()
? query ("NULL")
: "plainto_tsquery (" + query::_val (q) + ")") +
+ "," +
+ query::_val (t) +
")";
}
@@ -133,7 +135,7 @@ handle (request& rq, response& rs)
<< SCRIPT << " " << ~SCRIPT
<< ~HEAD
<< BODY
- << DIV_HEADER (root, options_->logo (), options_->menu ())
+ << DIV_HEADER (options_->logo (), options_->menu (), root, tenant)
<< DIV(ID="content");
session sn;
@@ -141,17 +143,19 @@ handle (request& rq, response& rs)
auto pkg_count (
package_db_->query_value<latest_package_count> (
- search_param<latest_package_count> (squery)));
+ search_param<latest_package_count> (squery, tenant)));
s << FORM_SEARCH (squery)
<< DIV_COUNTER (pkg_count, "Package", "Packages");
// Enclose the subsequent tables to be able to use nth-child CSS selector.
//
+ // @@ TENANT: use tenant for sorting when add support for global view.
+ //
s << DIV;
for (const auto& pr:
package_db_->query<latest_package_search_rank> (
- search_param<latest_package_search_rank> (squery) +
+ search_param<latest_package_search_rank> (squery, tenant) +
"ORDER BY rank DESC, name" +
"OFFSET" + to_string (page * res_page) +
"LIMIT" + to_string (res_page)))
@@ -160,11 +164,11 @@ handle (request& rq, response& rs)
s << TABLE(CLASS="proplist package")
<< TBODY
- << TR_NAME (p->id.name, squery_param, root)
+ << TR_NAME (p->name, squery_param, root, tenant)
<< TR_SUMMARY (p->summary)
<< TR_LICENSE (p->license_alternatives)
- << TR_TAGS (p->project, p->tags, root)
- << TR_DEPENDS (p->dependencies, root)
+ << TR_TAGS (p->project, p->tags, root, tenant)
+ << TR_DEPENDS (p->dependencies, root, tenant)
<< TR_REQUIRES (p->requirements)
<< ~TBODY
<< ~TABLE;
@@ -174,7 +178,7 @@ handle (request& rq, response& rs)
t.commit ();
s << DIV_PAGER (page, pkg_count, res_page, options_->search_pages (),
- root.string () + squery_param)
+ tenant_dir (root, tenant).string () + squery_param)
<< ~DIV
<< ~BODY
<< ~HTML;
diff --git a/mod/mod-package-version-details.cxx b/mod/mod-package-version-details.cxx
index d2d96d2..ee7457a 100644
--- a/mod/mod-package-version-details.cxx
+++ b/mod/mod-package-version-details.cxx
@@ -131,7 +131,7 @@ handle (request& rq, response& rs)
try
{
- pkg = package_db_->load<package> (package_id (pn, ver));
+ pkg = package_db_->load<package> (package_id (tenant, pn, ver));
// If the requested package turned up to be an "external" one just
// respond that no "internal" package is present.
@@ -147,7 +147,7 @@ handle (request& rq, response& rs)
throw invalid_request (
404, "Package '" + pn.string () + ' ' + sver + "' not found");
- const string& name (pkg->id.name.string ());
+ const string& name (pkg->name.string ());
const string title (name + " " + sver);
xml::serializer s (rs.content (), title);
@@ -158,7 +158,7 @@ handle (request& rq, response& rs)
<< CSS_LINKS (path ("package-version-details.css"), root)
<< ~HEAD
<< BODY
- << DIV_HEADER (root, options_->logo (), options_->menu ())
+ << DIV_HEADER (options_->logo (), options_->menu (), root, tenant)
<< DIV(ID="content");
if (full)
@@ -166,7 +166,8 @@ handle (request& rq, response& rs)
s << DIV(ID="heading")
<< H1
- << A(HREF=root / path (mime_url_encode (name, false)))
+ << A(HREF=tenant_dir (root, tenant) /
+ path (mime_url_encode (name, false)))
<< name
<< ~A
<< "/"
@@ -195,7 +196,7 @@ handle (request& rq, response& rs)
<< TR_PRIORITY (pkg->priority)
<< TR_LICENSES (pkg->license_alternatives)
- << TR_REPOSITORY (rl.canonical_name (), root)
+ << TR_REPOSITORY (rl.canonical_name (), root, tenant)
<< TR_LOCATION (rl);
if (rl.type () == repository_type::pkg)
@@ -216,7 +217,7 @@ handle (request& rq, response& rs)
<< TABLE(CLASS="proplist", ID="package")
<< TBODY
- << TR_PROJECT (pkg->project, root);
+ << TR_PROJECT (pkg->project, root, tenant);
const auto& u (pkg->url);
@@ -246,7 +247,7 @@ handle (request& rq, response& rs)
if (be && ((pe && be != pe) || (!pe && be != em)))
s << TR_EMAIL (*be, "build-email");
- s << TR_TAGS (pkg->tags, root)
+ s << TR_TAGS (pkg->tags, root, tenant)
<< ~TBODY
<< ~TABLE;
@@ -306,7 +307,7 @@ handle (request& rq, response& rs)
}
else if (p->internal ())
{
- dir_path u (root / dir_path (ename));
+ dir_path u (tenant_dir (root, tenant) / dir_path (ename));
s << A(HREF=u) << dname << ~A;
if (dcon)
diff --git a/mod/mod-repository-details.cxx b/mod/mod-repository-details.cxx
index 36d5508..6d3b8c1 100644
--- a/mod/mod-repository-details.cxx
+++ b/mod/mod-repository-details.cxx
@@ -82,7 +82,7 @@ handle (request& rq, response& rs)
<< CSS_LINKS (path ("repository-details.css"), root)
<< ~HEAD
<< BODY
- << DIV_HEADER (root, options_->logo (), options_->menu ())
+ << DIV_HEADER (options_->logo (), options_->menu (), root, tenant)
<< DIV(ID="content");
transaction t (package_db_->begin ());
@@ -91,13 +91,14 @@ handle (request& rq, response& rs)
for (const auto& r:
package_db_->query<repository> (
- query::internal + "ORDER BY" + query::priority))
+ (query::internal && query::id.tenant == tenant) +
+ "ORDER BY" + query::priority))
{
//@@ Feels like a lot of trouble (e.g., id_attribute()) for very
// dubious value. A link to the package search page just for
// this repository would probably be more useful.
//
- string id (html_id (r.name));
+ string id (html_id (r.canonical_name));
s << H1(ID=id)
<< A(HREF="#" + web::mime_url_encode (id, false))
<< r.display_name
@@ -144,9 +145,9 @@ handle (request& rq, response& rs)
//
s << P << "REPOSITORY CERTIFICATE" << ~P
<< P
- << "CN=" << cert.name.c_str () + np + 1 << *BR
- << "O=" << cert.organization << *BR
- << email (cert.email)
+ << "CN=" << cert.name.c_str () + np + 1 << *BR
+ << "O=" << cert.organization << *BR
+ << email (cert.email)
<< ~P
<< P(CLASS="certfp") << cert.fingerprint << ~P
<< PRE(CLASS="certpem") << cert.pem << ~PRE;
diff --git a/mod/mod-repository-root.cxx b/mod/mod-repository-root.cxx
index 3b0ab1f..b8777fd 100644
--- a/mod/mod-repository-root.cxx
+++ b/mod/mod-repository-root.cxx
@@ -275,13 +275,27 @@ namespace brep
if (!rpath.sub (root))
return false;
- const path& lpath (rpath.leaf (root));
+ path lpath (rpath.leaf (root));
+
+ if (!lpath.empty ())
+ {
+ path::iterator i (lpath.begin ());
+ const string& s (*i);
+
+ if (s[0] == '@' && s.size () > 1)
+ {
+ tenant = string (s, 1);
+ lpath = path (++i, lpath.end ());
+ }
+ }
// Delegate the request handling to the selected sub-handler. Intercept
// exception handling to add sub-handler attribution.
//
- auto handle = [&rq, &rs, this] (const char* nm, bool fn = false) -> bool
+ auto handle = [&rq, &rs, this] (const char* nm, bool fn = false)
{
+ handler_->tenant = move (tenant);
+
try
{
// Delegate the handling straight away if the sub-handler is not a
diff --git a/mod/mod-submit.cxx b/mod/mod-submit.cxx
index 470bd45..1b93756 100644
--- a/mod/mod-submit.cxx
+++ b/mod/mod-submit.cxx
@@ -201,7 +201,7 @@ handle (request& rq, response& rs)
<< CSS_LINKS (path ("submit.css"), root)
<< ~HEAD
<< BODY
- << DIV_HEADER (root, options_->logo (), options_->menu ())
+ << DIV_HEADER (options_->logo (), options_->menu (), root, tenant)
<< DIV(ID="content") << *form_ << ~DIV
<< ~BODY
<< ~HTML;
diff --git a/mod/module.hxx b/mod/module.hxx
index 127cdab..25dce43 100644
--- a/mod/module.hxx
+++ b/mod/module.hxx
@@ -10,6 +10,7 @@
#include <libbrep/types.hxx>
#include <libbrep/utility.hxx>
+#include <mod/utility.hxx>
#include <mod/options.hxx>
#include <mod/diagnostics.hxx>
@@ -70,6 +71,13 @@ namespace brep
//
class handler: public web::handler
{
+ public:
+ // If not empty, denotes the repository tenant the request is for.
+ // Extracted by the handler implementation from the request (URL path,
+ // parameters, etc).
+ //
+ string tenant;
+
// Diagnostics.
//
protected:
@@ -89,7 +97,7 @@ namespace brep
// Set to true when the handler is successfully initialized.
//
- bool initialized_ {false};
+ bool initialized_ = false;
// Implementation details.
//
diff --git a/mod/page.cxx b/mod/page.cxx
index 46b5e71..46f4879 100644
--- a/mod/page.cxx
+++ b/mod/page.cxx
@@ -18,6 +18,7 @@
#include <libbrep/package.hxx>
#include <libbrep/package-odb.hxx>
+#include <mod/utility.hxx>
#include <mod/build-config.hxx> // build_log_url()
using namespace std;
@@ -61,11 +62,14 @@ namespace brep
s << DIV(ID="header-menu")
<< DIV(ID="header-menu-body");
+ dir_path root (tenant_dir (root_, tenant_));
+
for (const auto& m: menu_)
{
- const string& l (m.link[0] == '/' || m.link.find (':') != string::npos
- ? m.link
- : root_.string () + m.link);
+ const string& l (
+ m.link[0] == '/' || m.link.find (':') != string::npos
+ ? m.link
+ : root.string () + m.link);
s << A(HREF=l) << m.label << ~A;
}
@@ -191,7 +195,8 @@ namespace brep
// Propagate search criteria to the package details page.
//
- << root_ / path (mime_url_encode (name_.string (), false))
+ << tenant_dir (root_, tenant_) /
+ path (mime_url_encode (name_.string (), false))
<< query_param_
<< ~HREF
@@ -221,8 +226,9 @@ namespace brep
}
else
{
- assert (root_ != nullptr);
- s << A(HREF=*root_ /
+ assert (root_ != nullptr && tenant_ != nullptr);
+
+ s << A(HREF=tenant_dir (*root_, *tenant_) /
dir_path (mime_url_encode (package_->string (), false)) /
path (version_))
<< version_;
@@ -249,7 +255,8 @@ namespace brep
<< SPAN(CLASS="value")
<< A
<< HREF
- << root_ << "?q=" << mime_url_encode (project_.string ())
+ << tenant_dir (root_, tenant_) << "?q="
+ << mime_url_encode (project_.string ())
<< ~HREF
<< project_
<< ~A
@@ -347,7 +354,10 @@ namespace brep
auto print = [&s, this] (const string& t)
{
- s << A << HREF << root_ << "?q=" << mime_url_encode (t) << ~HREF
+ s << A
+ << HREF
+ << tenant_dir (root_, tenant_) << "?q=" << mime_url_encode (t)
+ << ~HREF
<< t
<< ~A;
};
@@ -439,7 +449,7 @@ namespace brep
if (r->interface_url)
s << A(HREF=*r->interface_url + en) << n << ~A;
else if (p->internal ())
- s << A(HREF=root_ / path (en)) << n << ~A;
+ s << A(HREF=tenant_dir (root_, tenant_) / path (en)) << n << ~A;
else
// Display the dependency as plain text if no repository URL
// available.
@@ -586,7 +596,8 @@ namespace brep
<< SPAN(CLASS="value")
<< A
<< HREF
- << root_ << "?about#" << mime_url_encode (html_id (name_), false)
+ << tenant_dir (root_, tenant_) << "?about#"
+ << mime_url_encode (html_id (name_), false)
<< ~HREF
<< name_
<< ~A
diff --git a/mod/page.hxx b/mod/page.hxx
index 759984e..d3e10db 100644
--- a/mod/page.hxx
+++ b/mod/page.hxx
@@ -44,18 +44,20 @@ namespace brep
class DIV_HEADER
{
public:
- DIV_HEADER (const dir_path& root,
- const web::xhtml::fragment& logo,
- const vector<page_menu>& menu):
- root_ (root), logo_ (logo), menu_ (menu) {}
+ DIV_HEADER (const web::xhtml::fragment& logo,
+ const vector<page_menu>& menu,
+ const dir_path& root,
+ const string& tenant):
+ logo_ (logo), menu_ (menu), root_ (root), tenant_ (tenant) {}
void
operator() (xml::serializer&) const;
private:
- const dir_path& root_;
const web::xhtml::fragment& logo_;
const vector<page_menu>& menu_;
+ const dir_path& root_;
+ const string& tenant_;
};
// Generates package search form element.
@@ -163,8 +165,11 @@ namespace brep
class TR_NAME
{
public:
- TR_NAME (const package_name& n, const string& q, const dir_path& r)
- : name_ (n), query_param_ (q), root_ (r) {}
+ TR_NAME (const package_name& n,
+ const string& q,
+ const dir_path& r,
+ const string& t)
+ : name_ (n), query_param_ (q), root_ (r), tenant_ (t) {}
void
operator() (xml::serializer&) const;
@@ -173,6 +178,7 @@ namespace brep
const package_name& name_;
const string& query_param_;
const dir_path& root_;
+ const string& tenant_;
};
// Generates package version element.
@@ -182,11 +188,15 @@ namespace brep
public:
// Display the version as a link to the package version details page.
//
- TR_VERSION (const package_name& p, const version& v, const dir_path& r)
+ TR_VERSION (const package_name& p,
+ const version& v,
+ const dir_path& r,
+ const string& t)
: package_ (&p),
version_ (v.string ()),
stub_ (v.compare (wildcard_version, true) == 0),
- root_ (&r)
+ root_ (&r),
+ tenant_ (&t)
{
}
@@ -196,7 +206,8 @@ namespace brep
: package_ (nullptr),
version_ (v.string ()),
stub_ (v.compare (wildcard_version, true) == 0),
- root_ (nullptr)
+ root_ (nullptr),
+ tenant_ (nullptr)
{
}
@@ -208,6 +219,7 @@ namespace brep
string version_;
bool stub_;
const dir_path* root_;
+ const string* tenant_;
};
// Generates package project name element.
@@ -218,8 +230,8 @@ namespace brep
class TR_PROJECT
{
public:
- TR_PROJECT (const package_name& p, const dir_path& r)
- : project_ (p), root_ (r) {}
+ TR_PROJECT (const package_name& p, const dir_path& r, const string& t)
+ : project_ (p), root_ (r), tenant_ (t) {}
void
operator() (xml::serializer&) const;
@@ -227,6 +239,7 @@ namespace brep
private:
const package_name& project_;
const dir_path& root_;
+ const string& tenant_;
};
// Generates package summary element.
@@ -279,14 +292,17 @@ namespace brep
public:
// Display the tag link list.
//
- TR_TAGS (const strings& ts, const dir_path& r)
- : project_ (nullptr), tags_ (ts), root_ (r) {}
+ TR_TAGS (const strings& ts, const dir_path& r, const string& t)
+ : project_ (nullptr), tags_ (ts), root_ (r), tenant_ (t) {}
// As above but prepend the list with a tag link produced from the project
// name, if it differs from other tags.
//
- TR_TAGS (const package_name& pr, const strings& ts, const dir_path& r)
- : project_ (&pr), tags_ (ts), root_ (r) {}
+ TR_TAGS (const package_name& pr,
+ const strings& ts,
+ const dir_path& r,
+ const string& t)
+ : project_ (&pr), tags_ (ts), root_ (r), tenant_ (t) {}
void
operator() (xml::serializer&) const;
@@ -295,6 +311,7 @@ namespace brep
const package_name* project_;
const strings& tags_;
const dir_path& root_;
+ const string& tenant_;
};
// Generates package dependencies element.
@@ -302,8 +319,8 @@ namespace brep
class TR_DEPENDS
{
public:
- TR_DEPENDS (const dependencies& d, const dir_path& r)
- : dependencies_ (d), root_ (r) {}
+ TR_DEPENDS (const dependencies& d, const dir_path& r, const string& t)
+ : dependencies_ (d), root_ (r), tenant_ (t) {}
void
operator() (xml::serializer&) const;
@@ -311,6 +328,7 @@ namespace brep
private:
const dependencies& dependencies_;
const dir_path& root_;
+ const string& tenant_;
};
// Generates package requirements element.
@@ -377,8 +395,8 @@ namespace brep
class TR_REPOSITORY
{
public:
- TR_REPOSITORY (const string& n, const dir_path& r)
- : name_ (n), root_ (r) {}
+ TR_REPOSITORY (const string& n, const dir_path& r, const string& t)
+ : name_ (n), root_ (r), tenant_ (t) {}
void
operator() (xml::serializer&) const;
@@ -386,6 +404,7 @@ namespace brep
private:
const string& name_;
const dir_path& root_;
+ const string& tenant_;
};
// Generates repository location element.
diff --git a/mod/utility.hxx b/mod/utility.hxx
new file mode 100644
index 0000000..2ce1e07
--- /dev/null
+++ b/mod/utility.hxx
@@ -0,0 +1,25 @@
+// file : mod/utility.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef MOD_UTILITY_HXX
+#define MOD_UTILITY_HXX
+
+#include <libbrep/types.hxx>
+#include <libbrep/utility.hxx>
+
+namespace brep
+{
+ // Append the @<tenant> leaf component to the directory if the tenant is
+ // not empty. Otherwise, return the directory unchanged.
+ //
+ inline dir_path
+ tenant_dir (const dir_path& dir, const string& tenant)
+ {
+ return !tenant.empty ()
+ ? path_cast<dir_path> (dir / ('@' + tenant))
+ : dir;
+ }
+}
+
+#endif // MOD_UTILITY_HXX