Skip to content

Commit

Permalink
Merge pull request #38323 from elpaso/server-landingpage-dirwatcher
Browse files Browse the repository at this point in the history
Server landingpage dirwatcher
  • Loading branch information
elpaso committed Aug 17, 2020
2 parents ee14d3d + a04bbe4 commit 0bd81d2
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 28 deletions.
4 changes: 1 addition & 3 deletions src/server/services/landingpage/qgslandingpagehandlers.cpp
Expand Up @@ -69,9 +69,7 @@ json QgsLandingPageHandler::projectsData() const
const auto constProjectKeys { availableProjects.keys() };
for ( const auto &p : constProjectKeys )
{
auto info { QgsLandingPageUtils::projectInfo( availableProjects[ p ] ) };
info[ "id" ] = p.toStdString();
j.push_back( info );
j.push_back( QgsLandingPageUtils::projectInfo( availableProjects[ p ] ) );
}
return j;
}
Expand Down
89 changes: 66 additions & 23 deletions src/server/services/landingpage/qgslandingpageutils.cpp
Expand Up @@ -24,23 +24,65 @@
#include "qgsvectorlayer.h"
#include "nlohmann/json.hpp"

#include <mutex>
#include <QCryptographicHash>
#include <QFileSystemWatcher>

const QRegularExpression QgsLandingPageUtils::PROJECT_HASH_RE { QStringLiteral( "/(?<projectHash>[a-f0-9]{32})" ) };
QMap<QString, QString> QgsLandingPageUtils::AVAILABLE_PROJECTS;

std::once_flag initDirWatcher;

QMap<QString, QString> QgsLandingPageUtils::projects( )
{
// TODO: use a dir-watcher to invalidate

static QString QGIS_SERVER_PROJECTS_DIRECTORIES;
static QString QGIS_SERVER_PROJECTS_PG_CONNECTIONS;

// Init directory watcher
static QFileSystemWatcher dirWatcher;
std::call_once( initDirWatcher, [ = ]
{
QObject::connect( &dirWatcher, &QFileSystemWatcher::directoryChanged, qApp, [ = ]( const QString & path )
{
QgsMessageLog::logMessage( QStringLiteral( "Directory '%1' has changed: project information cache cleared." ).arg( path ), QStringLiteral( "Landing Page" ), Qgis::MessageLevel::Info );
AVAILABLE_PROJECTS.clear();
} );
} );


const QString projectDir { QString( qgetenv( "QGIS_SERVER_PROJECTS_DIRECTORIES" ) ) };

// Clear cache if QGIS_SERVER_PROJECTS_DIRECTORIES has changed
if ( projectDir != QGIS_SERVER_PROJECTS_DIRECTORIES )
{
QGIS_SERVER_PROJECTS_DIRECTORIES = projectDir;
AVAILABLE_PROJECTS.clear();
const QStringList cWatchedDirs { dirWatcher.directories() };
dirWatcher.removePaths( cWatchedDirs );
}

const QString pgConnections { QString( qgetenv( "QGIS_SERVER_PROJECTS_PG_CONNECTIONS" ) ) };

// Clear cache if QGIS_SERVER_PROJECTS_PG_CONNECTIONS has changed
if ( pgConnections != QGIS_SERVER_PROJECTS_PG_CONNECTIONS )
{
QGIS_SERVER_PROJECTS_PG_CONNECTIONS = pgConnections;
AVAILABLE_PROJECTS.clear();
}

// Scan QGIS_SERVER_PROJECTS_DIRECTORIES
if ( AVAILABLE_PROJECTS.isEmpty() )
{
for ( const auto &path : QString( qgetenv( "QGIS_SERVER_PROJECTS_DIRECTORIES" ) ).split( QStringLiteral( "||" ) ) )
const auto cProjectDirs { projectDir.split( QStringLiteral( "||" ) ) };
for ( const auto &path : cProjectDirs )
{
if ( ! path.isEmpty() )
{
const QDir dir { path };
if ( dir.exists() )
{
dirWatcher.addPath( dir.path() );
const auto constFiles { dir.entryList( ) };
for ( const auto &f : constFiles )
{
Expand All @@ -64,35 +106,36 @@ QMap<QString, QString> QgsLandingPageUtils::projects( )
QgsMessageLog::logMessage( QStringLiteral( "QGIS_SERVER_PROJECTS_DIRECTORIES empty path: skipping." ), QStringLiteral( "Landing Page" ), Qgis::MessageLevel::Warning );
}
}
}

// PG projects
const auto storage { QgsApplication::instance()->projectStorageRegistry()->projectStorageFromType( QStringLiteral( "postgresql" ) ) };
Q_ASSERT( storage );
for ( const auto &connectionString : QString( qgetenv( "QGIS_SERVER_PROJECTS_PG_CONNECTIONS" ) ).split( QStringLiteral( "||" ) ) )
// PG projects (there is no watcher for PG: scan every time)
const auto storage { QgsApplication::instance()->projectStorageRegistry()->projectStorageFromType( QStringLiteral( "postgresql" ) ) };
Q_ASSERT( storage );
const auto cPgConnections { pgConnections.split( QStringLiteral( "||" ) ) };
for ( const auto &connectionString : cPgConnections )
{
if ( ! connectionString.isEmpty() )
{
if ( ! connectionString.isEmpty() )
const auto constProjects { storage->listProjects( connectionString ) };
if ( ! constProjects.isEmpty() )
{
const auto constProjects { storage->listProjects( connectionString ) };
if ( ! constProjects.isEmpty() )
{
for ( const auto &projectName : constProjects )
{
const QString projectFullPath { connectionString + QStringLiteral( "&project=%1" ).arg( projectName ) };
const auto projectHash { QCryptographicHash::hash( projectFullPath.toUtf8(), QCryptographicHash::Md5 ).toHex() };
AVAILABLE_PROJECTS[ projectHash ] = projectFullPath;
QgsMessageLog::logMessage( QStringLiteral( "Adding postgres project '%1' with id '%2'" ).arg( projectName, QString::fromUtf8( projectHash ) ), QStringLiteral( "Landing Page" ), Qgis::MessageLevel::Warning );
}
}
else
for ( const auto &projectName : constProjects )
{
QgsMessageLog::logMessage( QStringLiteral( "QGIS_SERVER_PROJECTS_PG_CONNECTIONS entry '%1' was not found or has not projects: skipping." ).arg( connectionString ), QStringLiteral( "Landing Page" ), Qgis::MessageLevel::Warning );
const QString projectFullPath { connectionString + QStringLiteral( "&project=%1" ).arg( projectName ) };
const auto projectHash { QCryptographicHash::hash( projectFullPath.toUtf8(), QCryptographicHash::Md5 ).toHex() };
AVAILABLE_PROJECTS[ projectHash ] = projectFullPath;
QgsMessageLog::logMessage( QStringLiteral( "Adding postgres project '%1' with id '%2'" ).arg( projectName, QString::fromUtf8( projectHash ) ), QStringLiteral( "Landing Page" ), Qgis::MessageLevel::Warning );
}
}
else
{
QgsMessageLog::logMessage( QStringLiteral( "QGIS_SERVER_PROJECTS_PG_CONNECTIONS empty connection: skipping." ), QStringLiteral( "Landing Page" ), Qgis::MessageLevel::Warning );
QgsMessageLog::logMessage( QStringLiteral( "QGIS_SERVER_PROJECTS_PG_CONNECTIONS entry '%1' was not found or has not projects: skipping." ).arg( connectionString ), QStringLiteral( "Landing Page" ), Qgis::MessageLevel::Warning );
}
}
else
{
QgsMessageLog::logMessage( QStringLiteral( "QGIS_SERVER_PROJECTS_PG_CONNECTIONS empty connection: skipping." ), QStringLiteral( "Landing Page" ), Qgis::MessageLevel::Warning );
}
}

return AVAILABLE_PROJECTS;
Expand Down Expand Up @@ -176,8 +219,8 @@ json QgsLandingPageUtils::projectInfo( const QString &projectUri )
return jLinks;
};

json info;
info[ "id" ] = QCryptographicHash::hash( projectUri.toUtf8(), QCryptographicHash::Md5 ).toHex();
json info = json::object();
info[ "id" ] = QCryptographicHash::hash( projectUri.toUtf8(), QCryptographicHash::Md5 ).toHex();
QgsProject p;
if ( p.read( projectUri ) )
{
Expand Down
18 changes: 16 additions & 2 deletions tests/src/python/test_qgsserver_landingpage.py
Expand Up @@ -54,8 +54,13 @@ class QgsServerLandingPageTest(QgsServerAPITestBase):
@classmethod
def setUpClass(cls):
super().setUpClass()
directories = [os.path.join(unitTestDataPath('qgis_server'), 'landingpage', 'projects')]
directories.append(os.path.join(unitTestDataPath('qgis_server'), 'landingpage', 'projects2'))

cls.temp_dir = QtCore.QTemporaryDir()

temp_dir = cls.temp_dir.path()
shutil.copytree(os.path.join(unitTestDataPath('qgis_server'), 'landingpage'), os.path.join(temp_dir, 'landingpage'))

directories = [os.path.join(temp_dir, 'landingpage', 'projects'), os.path.join(temp_dir, 'landingpage', 'projects2')]
os.environ['QGIS_SERVER_PROJECTS_DIRECTORIES'] = '||'.join(directories)

if not os.environ.get('TRAVIS', False):
Expand Down Expand Up @@ -149,6 +154,15 @@ def test_project_json(self):
self.compareApi(
request, None, 'test_project_{}.json'.format(name.replace('.', '_')), subdir='landingpage')

def test_landing_page_json_empty(self):
"""Test landing page in JSON format with no projects"""

os.environ['QGIS_SERVER_PROJECTS_DIRECTORIES'] = ''
os.environ['QGIS_SERVER_PROJECTS_PG_CONNECTIONS'] = ''
request = QgsBufferServerRequest('http://server.qgis.org/index.json')
self.compareApi(
request, None, 'test_landing_page_empty_index.json', subdir='landingpage')


if __name__ == '__main__':
unittest.main()
Binary file not shown.
@@ -0,0 +1,21 @@
Content-Type: application/json

{
"links": [
{
"href": "http://server.qgis.org/index.json",
"rel": "self",
"title": "Landing page as JSON",
"type": "application/json"
},
{
"href": "http://server.qgis.org/index.html",
"rel": "alternate",
"title": "Landing page as HTML",
"type": "text/html"
}
],
"projects": [],
"projects_count": 0,
"timeStamp": "2019-07-05T12:27:07Z"
}

0 comments on commit 0bd81d2

Please sign in to comment.