aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2018-09-13 19:27:00 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2018-09-14 13:55:17 +0300
commit1367aa09951e0aa7491bc2a5bf7209b0b47be65e (patch)
tree8e4ffe92d9d7107d712f7096a7eb78872617efca
parentda39917917f03bd709eb7807cc5d900ddeb1e48c (diff)
Add support for packages and builds global views
-rw-r--r--etc/brep-module.conf6
-rw-r--r--libbrep/package-extra.sql21
-rw-r--r--libbrep/package.cxx54
-rw-r--r--libbrep/package.hxx4
-rw-r--r--mod/mod-build-log.cxx7
-rw-r--r--mod/mod-builds.cxx68
-rw-r--r--mod/mod-packages.cxx35
-rw-r--r--mod/options.cli7
-rw-r--r--mod/page.cxx18
-rw-r--r--mod/page.hxx23
-rw-r--r--www/builds-body.css3
-rw-r--r--www/packages-body.css3
12 files changed, 176 insertions, 73 deletions
diff --git a/etc/brep-module.conf b/etc/brep-module.conf
index 5602d61..b4c0ee4 100644
--- a/etc/brep-module.conf
+++ b/etc/brep-module.conf
@@ -326,6 +326,12 @@ menu About=?about
# root-tenant-view packages
+# Name to call the tenant values on web pages. If not specified, then 'tenant'
+# is used.
+#
+# tenant-name tenant
+
+
# Trace verbosity. Disabled by default.
#
# verbosity 0
diff --git a/libbrep/package-extra.sql b/libbrep/package-extra.sql
index d9930aa..93c61d8 100644
--- a/libbrep/package-extra.sql
+++ b/libbrep/package-extra.sql
@@ -30,7 +30,8 @@ DROP FUNCTION IF EXISTS latest_packages(IN tenant TEXT);
DROP TYPE IF EXISTS weighted_text CASCADE;
CREATE TYPE weighted_text AS (a TEXT, b TEXT, c TEXT, d TEXT);
--- Return the latest versions of internal packages as a set of package rows.
+-- Return the latest versions of matching a tenant internal packages as a set
+-- of package rows. If tenant is NULL, then match all tenants.
--
CREATE FUNCTION
latest_packages(IN tenant TEXT)
@@ -49,7 +50,7 @@ RETURNS SETOF package AS $$
p1.version_canonical_release = p2.version_canonical_release AND
p1.version_revision < p2.version_revision))))
WHERE
- p1.tenant = latest_packages.tenant AND
+ (latest_packages.tenant IS NULL OR p1.tenant = latest_packages.tenant) AND
p1.internal_repository_canonical_name IS NOT NULL AND
p2.name IS NULL;
$$ LANGUAGE SQL STABLE;
@@ -72,10 +73,10 @@ RETURNS SETOF record AS $$
WHERE name = latest_package.name;
$$ LANGUAGE SQL STABLE;
--- Search for the latest version of an internal packages matching the specified
--- search query. Return a set of rows containing the package id and search
--- rank. If query is NULL, then match all packages and return 0 rank for
--- all rows.
+-- Search for the latest version of an internal packages matching the
+-- specified search query and tenant. Return a set of rows containing the
+-- package id and search rank. If query is NULL, then match all packages and
+-- return 0 rank for all rows. If tenant is NULL, then match all tenants.
--
CREATE FUNCTION
search_latest_packages(IN query tsquery,
@@ -98,10 +99,10 @@ RETURNS SETOF record AS $$
WHERE query IS NULL OR search_index @@ query;
$$ LANGUAGE SQL STABLE;
--- Search for packages matching the search query and having the specified
--- tenant and name. Return a set of rows containing the package id and search
+-- Search for packages matching the search query and tenant and having the
+-- specified name. Return a set of rows containing the package id and search
-- rank. If query is NULL, then match all packages and return 0 rank for all
--- rows.
+-- rows. If tenant is NULL, then match all tenants.
--
CREATE FUNCTION
search_packages(IN query tsquery,
@@ -122,7 +123,7 @@ RETURNS SETOF record AS $$
END AS rank
FROM package
WHERE
- tenant = search_packages.tenant AND
+ (search_packages.tenant IS NULL OR tenant = search_packages.tenant) AND
name = search_packages.name AND
internal_repository_canonical_name IS NOT NULL AND
(query IS NULL OR search_index @@ query);
diff --git a/libbrep/package.cxx b/libbrep/package.cxx
index 41dd4e2..e3921fe 100644
--- a/libbrep/package.cxx
+++ b/libbrep/package.cxx
@@ -64,32 +64,33 @@ namespace brep
optional<string> fr,
optional<string> sh,
shared_ptr<repository_type> rp)
- : id (rp->tenant, move (nm), vr),
- name (id.name),
- version (move (vr)),
- project (move (pn)),
- priority (move (pr)),
- summary (move (sm)),
- license_alternatives (move (la)),
- tags (move (tg)),
- description (move (ds)),
- changes (move (ch)),
- url (move (ur)),
- doc_url (move (du)),
- src_url (move (su)),
- package_url (move (pu)),
- email (move (em)),
- package_email (move (pe)),
- build_email (move (be)),
- dependencies (move (dp)),
- requirements (move (rq)),
- build_constraints (version.compare (wildcard_version, true) != 0
- ? move (bc)
- : build_constraints_type ()),
- internal_repository (move (rp)),
- location (move (lc)),
- fragment (move (fr)),
- sha256sum (move (sh))
+ : id (rp->tenant, move (nm), vr),
+ tenant (id.tenant),
+ name (id.name),
+ version (move (vr)),
+ project (move (pn)),
+ priority (move (pr)),
+ summary (move (sm)),
+ license_alternatives (move (la)),
+ tags (move (tg)),
+ description (move (ds)),
+ changes (move (ch)),
+ url (move (ur)),
+ doc_url (move (du)),
+ src_url (move (su)),
+ package_url (move (pu)),
+ email (move (em)),
+ package_email (move (pe)),
+ build_email (move (be)),
+ dependencies (move (dp)),
+ requirements (move (rq)),
+ build_constraints (version.compare (wildcard_version, true) != 0
+ ? move (bc)
+ : build_constraints_type ()),
+ internal_repository (move (rp)),
+ location (move (lc)),
+ fragment (move (fr)),
+ sha256sum (move (sh))
{
assert (internal_repository->internal);
}
@@ -99,6 +100,7 @@ namespace brep
version_type vr,
shared_ptr<repository_type> rp)
: id (rp->tenant, move (nm), vr),
+ tenant (id.tenant),
name (id.name),
version (move (vr))
{
diff --git a/libbrep/package.hxx b/libbrep/package.hxx
index 6e372b3..fbe6f60 100644
--- a/libbrep/package.hxx
+++ b/libbrep/package.hxx
@@ -346,6 +346,7 @@ namespace brep
//
package_id id;
+ const string& tenant; // Tracks id.tenant.
const package_name& name; // Tracks id.name.
upstream_version version;
@@ -395,6 +396,7 @@ namespace brep
// Database mapping.
//
#pragma db member(id) id column("")
+ #pragma db member(tenant) transient
#pragma db member(name) transient
#pragma db member(version) set(this.version.init (this.id.version, (?)))
@@ -476,7 +478,7 @@ namespace brep
private:
friend class odb::access;
- package (): name (id.name) {}
+ package (): tenant (id.tenant), name (id.name) {}
// Save keywords, summary, description, and changes to weighted_text
// a, b, c, d members, respectively. So a word found in keywords will
diff --git a/mod/mod-build-log.cxx b/mod/mod-build-log.cxx
index 70e2c7e..ee5d1b2 100644
--- a/mod/mod-build-log.cxx
+++ b/mod/mod-build-log.cxx
@@ -212,10 +212,13 @@ handle (request& rq, response& rs)
//
ostream& os (rs.content (200, "text/plain;charset=utf-8", false));
- auto print_header = [&os, &b] ()
+ auto print_header = [&os, &b, this] ()
{
- // @@ Should we print the tenant? How to call it if that's the case?
+ // Print the build tenant in the multi-tenant mode.
//
+ if (!b->tenant.empty ())
+ os << options_->tenant_name () << ": " << b->tenant << endl << endl;
+
os << "package: " << b->package_name << endl
<< "version: " << b->package_version << endl
<< "toolchain: " << b->toolchain_name << '-' << b->toolchain_version
diff --git a/mod/mod-builds.cxx b/mod/mod-builds.cxx
index d2e3a25..a55b6f9 100644
--- a/mod/mod-builds.cxx
+++ b/mod/mod-builds.cxx
@@ -94,7 +94,7 @@ template <typename T>
static inline query<T>
build_query (const brep::cstrings& configs,
const brep::params::builds& params,
- const brep::optional<string>& tenant)
+ const brep::optional<brep::string>& tenant)
{
using namespace brep;
using query = query<T>;
@@ -207,12 +207,16 @@ build_query (const brep::cstrings& configs,
template <typename T, typename P = typename query<T>::build_package>
static inline query<T>
-package_query (const brep::params::builds& params, const string& tenant)
+package_query (const brep::params::builds& params,
+ const brep::optional<brep::string>& tenant)
{
using namespace brep;
using query = query<T>;
- query q (P::id.tenant == tenant);
+ query q (true);
+
+ if (tenant)
+ q = 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.
@@ -281,6 +285,7 @@ handle (request& rq, response& rs)
const size_t page_configs (options_->build_configurations ());
const string& host (options_->host ());
const dir_path& root (options_->root ());
+ const string& tenant_name (options_->tenant_name ());
params::builds params;
@@ -321,18 +326,25 @@ handle (request& rq, response& rs)
<< DIV_HEADER (options_->logo (), options_->menu (), root, tenant)
<< DIV(ID="content");
+ // If the tenant is empty then we are in the global view and will display
+ // builds from all the tenants.
+ //
+ optional<string> tn;
+ if (!tenant.empty ())
+ tn = tenant;
+
// Return the list of distinct toolchain name/version pairs. The build db
// transaction must be started.
//
using toolchains = vector<pair<string, version>>;
- auto query_toolchains = [this] () -> toolchains
+ auto query_toolchains = [this, &tn] () -> toolchains
{
using query = query<toolchain>;
toolchains r;
for (auto& t: build_db_->query<toolchain> (
- (query::id.package.tenant == tenant) +
+ (tn ? query::id.package.tenant == *tn : query (true)) +
"ORDER BY" + query::toolchain_name +
order_by_version_desc (query::id.toolchain_version, false)))
r.emplace_back (move (t.name), move (t.version));
@@ -428,7 +440,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, tenant));
+ build_query<package_build_count> (*build_conf_names_, params, tn));
// Print the filter form.
//
@@ -443,7 +455,7 @@ handle (request& rq, response& rs)
//
s << DIV;
for (auto& pb: build_db_->query<package_build> (
- build_query<package_build> (*build_conf_names_, params, tenant) +
+ build_query<package_build> (*build_conf_names_, params, tn) +
"ORDER BY" + query<package_build>::build::timestamp + "DESC" +
"OFFSET" + to_string (page * page_configs) +
"LIMIT" + to_string (page_configs)))
@@ -461,8 +473,8 @@ handle (request& rq, response& rs)
s << TABLE(CLASS="proplist build")
<< TBODY
- << TR_NAME (b.package_name, string (), root, tenant)
- << TR_VERSION (b.package_name, b.package_version, root, tenant)
+ << TR_NAME (b.package_name, string (), root, b.tenant)
+ << TR_VERSION (b.package_name, b.package_version, root, b.tenant)
<< TR_VALUE ("toolchain",
b.toolchain_name + '-' +
b.toolchain_version.string ())
@@ -470,8 +482,15 @@ handle (request& rq, response& rs)
<< TR_VALUE ("machine", b.machine)
<< TR_VALUE ("target", b.target.string ())
<< TR_VALUE ("timestamp", ts)
- << TR_BUILD_RESULT (b, host, root)
- << ~TBODY
+ << TR_BUILD_RESULT (b, host, root);
+
+ // In the global view mode add the tenant builds link. Note that the
+ // global view (and the link) makes sense only in the multi-tenant mode.
+ //
+ if (!tn && !b.tenant.empty ())
+ s << TR_TENANT (tenant_name, "builds", root, b.tenant);
+
+ s << ~TBODY
<< ~TABLE;
}
s << ~DIV;
@@ -589,12 +608,12 @@ handle (request& rq, response& rs)
size_t nmax (
config_toolchains.size () *
build_db_->query_value<buildable_package_count> (
- package_query<buildable_package_count> (params, tenant)));
+ package_query<buildable_package_count> (params, tn)));
size_t ncur = build_db_->query_value<package_build_count> (
build_query<package_build_count> (*build_conf_names_,
bld_params,
- tenant));
+ tn));
// From now we will be using specific package name and version for each
// build database query.
@@ -641,7 +660,7 @@ handle (request& rq, response& rs)
//
using query = query<build_constrained_package>;
query q (package_query<build_constrained_package, query> (params,
- tenant));
+ tn));
for (const auto& p: build_db_->query<build_constrained_package> (q))
{
@@ -696,18 +715,17 @@ 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, tenant));
+ pkg_query pq (package_query<buildable_package> (params, tn));
// 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) +
+ "," + pkg_query::build_package::id.tenant +
"OFFSET" + pkg_query::_ref (offset) + "LIMIT 50";
connection_ptr conn (build_db_->connection ());
@@ -834,14 +852,22 @@ handle (request& rq, response& rs)
s << TABLE(CLASS="proplist build")
<< TBODY
- << TR_NAME (id.name, string (), root, tenant)
- << TR_VERSION (id.name, p.version, root, tenant)
+ << TR_NAME (id.name, string (), root, id.tenant)
+ << TR_VERSION (id.name, p.version, root, id.tenant)
<< TR_VALUE ("toolchain",
string (ct.toolchain_name) + '-' +
ct.toolchain_version.string ())
<< TR_VALUE ("config", ct.configuration)
- << TR_VALUE ("target", i->second->target.string ())
- << ~TBODY
+ << TR_VALUE ("target", i->second->target.string ());
+
+ // In the global view mode add the tenant builds link. Note that
+ // the global view (and the link) makes sense only in the
+ // multi-tenant mode.
+ //
+ if (!tn && !id.tenant.empty ())
+ s << TR_TENANT (tenant_name, "builds", root, id.tenant);
+
+ s << ~TBODY
<< ~TABLE;
if (--print == 0) // Bail out the configuration loop.
diff --git a/mod/mod-packages.cxx b/mod/mod-packages.cxx
index 1515a53..27e1270 100644
--- a/mod/mod-packages.cxx
+++ b/mod/mod-packages.cxx
@@ -67,7 +67,7 @@ init (scanner& s)
template <typename T>
static inline query<T>
-search_param (const brep::string& q, const brep::string& t)
+search_param (const brep::string& q, const brep::optional<brep::string>& t)
{
using query = query<T>;
return "(" +
@@ -75,7 +75,7 @@ search_param (const brep::string& q, const brep::string& t)
? query ("NULL")
: "plainto_tsquery (" + query::_val (q) + ")") +
"," +
- query::_val (t) +
+ (!t ? query ("NULL") : query (query::_val (*t))) +
")";
}
@@ -89,6 +89,7 @@ handle (request& rq, response& rs)
const size_t res_page (options_->search_results ());
const dir_path& root (options_->root ());
const string& title (options_->search_title ());
+ const string& tenant_name (options_->tenant_name ());
params::packages params;
@@ -135,25 +136,30 @@ handle (request& rq, response& rs)
<< DIV_HEADER (options_->logo (), options_->menu (), root, tenant)
<< DIV(ID="content");
+ // If the tenant is empty then we are in the global view and will display
+ // packages from all the tenants.
+ //
+ optional<string> tn;
+ if (!tenant.empty ())
+ tn = tenant;
+
session sn;
transaction t (package_db_->begin ());
auto pkg_count (
package_db_->query_value<latest_package_count> (
- search_param<latest_package_count> (squery, tenant)));
+ search_param<latest_package_count> (squery, tn)));
s << FORM_SEARCH (squery, "packages")
<< 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, tenant) +
- "ORDER BY rank DESC, name" +
+ search_param<latest_package_search_rank> (squery, tn) +
+ "ORDER BY rank DESC, name, tenant" +
"OFFSET" + to_string (page * res_page) +
"LIMIT" + to_string (res_page)))
{
@@ -161,13 +167,20 @@ handle (request& rq, response& rs)
s << TABLE(CLASS="proplist package")
<< TBODY
- << TR_NAME (p->name, equery, root, tenant)
+ << TR_NAME (p->name, equery, root, p->tenant)
<< TR_SUMMARY (p->summary)
<< TR_LICENSE (p->license_alternatives)
<< TR_TAGS (p->project, p->tags, root, tenant)
- << TR_DEPENDS (p->dependencies, root, tenant)
- << TR_REQUIRES (p->requirements)
- << ~TBODY
+ << TR_DEPENDS (p->dependencies, root, p->tenant)
+ << TR_REQUIRES (p->requirements);
+
+ // In the global view mode add the tenant packages link. Note that the
+ // global view (and the link) makes sense only in the multi-tenant mode.
+ //
+ if (!tn && !p->tenant.empty ())
+ s << TR_TENANT (tenant_name, "packages", root, p->tenant);
+
+ s << ~TBODY
<< ~TABLE;
}
s << ~DIV;
diff --git a/mod/options.cli b/mod/options.cli
index 5fbd117..c78af73 100644
--- a/mod/options.cli
+++ b/mod/options.cli
@@ -46,6 +46,13 @@ namespace brep
(\cb{http://example.org/})."
}
+ string tenant-name = "tenant"
+ {
+ "<name>",
+ "Name to call the tenant values on web pages. If not specified, then
+ \cb{tenant} is used."
+ }
+
uint16_t verbosity = 0
{
"<level>",
diff --git a/mod/page.cxx b/mod/page.cxx
index ce12da6..1b2faae 100644
--- a/mod/page.cxx
+++ b/mod/page.cxx
@@ -182,6 +182,24 @@ namespace brep
<< ~TR;
}
+ // TR_TENANT
+ //
+ void TR_TENANT::
+ operator() (serializer& s) const
+ {
+ s << TR(CLASS="tenant")
+ << TH << name_ << ~TH
+ << TD
+ << SPAN(CLASS="value")
+ << A
+ << HREF << tenant_dir (root_, tenant_) << '?' << service_ << ~HREF
+ << tenant_
+ << ~A
+ << ~SPAN
+ << ~TD
+ << ~TR;
+ }
+
// TR_NAME
//
void TR_NAME::
diff --git a/mod/page.hxx b/mod/page.hxx
index d9f4249..4b59e7d 100644
--- a/mod/page.hxx
+++ b/mod/page.hxx
@@ -162,6 +162,29 @@ namespace brep
const vector<pair<string, string>>& options_;
};
+ // Generates tenant id element.
+ //
+ // Displays a link to the service page for the specified tenant.
+ //
+ class TR_TENANT
+ {
+ public:
+ TR_TENANT (const string& n,
+ const string& s,
+ const dir_path& r,
+ const string& t)
+ : name_ (n), service_ (s), root_ (r), tenant_ (t) {}
+
+ void
+ operator() (xml::serializer&) const;
+
+ private:
+ const string& name_;
+ const string& service_;
+ const dir_path& root_;
+ const string& tenant_;
+ };
+
// Generates package name element with an optional search criteria. The
// search string should be url-encoded, if specified.
//
diff --git a/www/builds-body.css b/www/builds-body.css
index ef641e8..6c27b09 100644
--- a/www/builds-body.css
+++ b/www/builds-body.css
@@ -47,7 +47,8 @@
.build tr.machine td .value,
.build tr.target td .value,
.build tr.timestamp td .value,
-.build tr.result td .value
+.build tr.result td .value,
+.build tr.tenant td .value
{
/* <code> style. */
font-family: monospace;
diff --git a/www/packages-body.css b/www/packages-body.css
index df77d14..79911d4 100644
--- a/www/packages-body.css
+++ b/www/packages-body.css
@@ -33,7 +33,8 @@
.package tr.name td .value,
.package tr.depends td .value,
-.package tr.requires td .value
+.package tr.requires td .value,
+.package tr.tenant td .value
{
/* <code> style. */
font-family: monospace;