mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-15 00:02:52 -04:00
Server: add QGIS_SERVER_IGNORE_BAD_LAYERS config option
Allow to override the default behavior in case of bad layers (which is to invalidate the whole project). When set to TRUE, the bad layers are skipped and the project is considered available. Followup #8922
This commit is contained in:
parent
1175555301
commit
b94988aef6
@ -36,12 +36,17 @@ Removes an entry from cache.
|
||||
:param path: The path of the project
|
||||
%End
|
||||
|
||||
const QgsProject *project( const QString &path );
|
||||
const QgsProject *project( const QString &path, QgsServerSettings *settings = 0 );
|
||||
%Docstring
|
||||
If the project is not cached yet, then the project is read thanks to the
|
||||
If the project is not cached yet, then the project is read from the
|
||||
path. If the project is not available, then ``None`` is returned.
|
||||
If the project contains any bad layer it is considered unavailable
|
||||
unless the server configuration variable QGIS_SERVER_IGNORE_BAD_LAYERS
|
||||
passed in the optional settings argument is set to ``True`` (the default
|
||||
value is ``False``).
|
||||
|
||||
:param path: the filename of the QGIS project
|
||||
:param settings: QGIS server settings
|
||||
|
||||
:return: the project or ``None`` if an error happened
|
||||
|
||||
|
@ -173,6 +173,17 @@ The default value is 10000, this value can be changed by setting the environment
|
||||
variable QGIS_SERVER_API_WFS3_MAX_LIMIT.
|
||||
|
||||
.. versionadded:: 3.10
|
||||
%End
|
||||
|
||||
bool ignoreBadLayers() const;
|
||||
%Docstring
|
||||
Returns ``True`` if the bad layers are ignored and ``False`` when the presence of a
|
||||
bad layers invalidates the whole project making it unavailable.
|
||||
|
||||
The default value is ``True``, this value can be changed by setting the environment
|
||||
variable QGIS_SERVER_IGNORE_BAD_LAYERS.
|
||||
|
||||
.. versionadded:: 3.10.5
|
||||
%End
|
||||
|
||||
};
|
||||
|
@ -38,7 +38,8 @@ QgsConfigCache::QgsConfigCache()
|
||||
QObject::connect( &mFileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &QgsConfigCache::removeChangedEntry );
|
||||
}
|
||||
|
||||
const QgsProject *QgsConfigCache::project( const QString &path )
|
||||
|
||||
const QgsProject *QgsConfigCache::project( const QString &path, QgsServerSettings *settings )
|
||||
{
|
||||
if ( ! mProjectCache[ path ] )
|
||||
{
|
||||
@ -68,10 +69,20 @@ const QgsProject *QgsConfigCache::project( const QString &path )
|
||||
}
|
||||
if ( !unrestrictedBadLayers.isEmpty() )
|
||||
{
|
||||
QgsMessageLog::logMessage(
|
||||
QStringLiteral( "Error, Layer(s) %1 not valid in project %2" ).arg( unrestrictedBadLayers.join( QStringLiteral( ", " ) ), path ),
|
||||
QStringLiteral( "Server" ), Qgis::Critical );
|
||||
throw QgsServerException( QStringLiteral( "Layer(s) not valid" ) );
|
||||
// This is a critical error unless QGIS_SERVER_IGNORE_BAD_LAYERS is set to TRUE
|
||||
if ( ! settings || ! settings->ignoreBadLayers() )
|
||||
{
|
||||
QgsMessageLog::logMessage(
|
||||
QStringLiteral( "Error, Layer(s) %1 not valid in project %2" ).arg( unrestrictedBadLayers.join( QStringLiteral( ", " ) ), path ),
|
||||
QStringLiteral( "Server" ), Qgis::Critical );
|
||||
throw QgsServerException( QStringLiteral( "Layer(s) not valid" ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsMessageLog::logMessage(
|
||||
QStringLiteral( "Warning, Layer(s) %1 not valid in project %2" ).arg( unrestrictedBadLayers.join( QStringLiteral( ", " ) ), path ),
|
||||
QStringLiteral( "Server" ), Qgis::Warning );
|
||||
}
|
||||
}
|
||||
}
|
||||
mProjectCache.insert( path, prj.release() );
|
||||
@ -86,6 +97,7 @@ const QgsProject *QgsConfigCache::project( const QString &path )
|
||||
}
|
||||
QgsProject::setInstance( mProjectCache[ path ] );
|
||||
return mProjectCache[ path ];
|
||||
|
||||
}
|
||||
|
||||
QDomDocument *QgsConfigCache::xmlDocument( const QString &filePath )
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "qgis_server.h"
|
||||
#include "qgis_sip.h"
|
||||
#include "qgsproject.h"
|
||||
#include "qgsserversettings.h"
|
||||
|
||||
/**
|
||||
* \ingroup server
|
||||
@ -51,13 +52,18 @@ class SERVER_EXPORT QgsConfigCache : public QObject
|
||||
void removeEntry( const QString &path );
|
||||
|
||||
/**
|
||||
* If the project is not cached yet, then the project is read thanks to the
|
||||
* If the project is not cached yet, then the project is read from the
|
||||
* path. If the project is not available, then NULLPTR is returned.
|
||||
* If the project contains any bad layer it is considered unavailable
|
||||
* unless the server configuration variable QGIS_SERVER_IGNORE_BAD_LAYERS
|
||||
* passed in the optional settings argument is set to TRUE (the default
|
||||
* value is FALSE).
|
||||
* \param path the filename of the QGIS project
|
||||
* \param settings QGIS server settings
|
||||
* \returns the project or NULLPTR if an error happened
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
const QgsProject *project( const QString &path );
|
||||
const QgsProject *project( const QString &path, QgsServerSettings *settings = nullptr );
|
||||
|
||||
private:
|
||||
QgsConfigCache() SIP_FORCE;
|
||||
|
@ -210,7 +210,8 @@ void QgsFcgiServerRequest::printRequestInfos( const QUrl &url )
|
||||
QStringLiteral( "HTTP_PROXY" ),
|
||||
QStringLiteral( "NO_PROXY" ),
|
||||
QStringLiteral( "HTTP_AUTHORIZATION" ),
|
||||
QStringLiteral( "QGIS_PROJECT_FILE" )
|
||||
QStringLiteral( "QGIS_PROJECT_FILE" ),
|
||||
QStringLiteral( "QGIS_SERVER_IGNORE_BAD_LAYERS" )
|
||||
};
|
||||
|
||||
QgsMessageLog::logMessage( QStringLiteral( "Request URL: %2" ).arg( url.url() ), QStringLiteral( "Server" ), Qgis::Info );
|
||||
|
@ -375,7 +375,7 @@ void QgsServer::handleRequest( QgsServerRequest &request, QgsServerResponse &res
|
||||
QString configFilePath = configPath( *sConfigFilePath, params.map() );
|
||||
|
||||
// load the project if needed and not empty
|
||||
project = mConfigCache->project( configFilePath );
|
||||
project = mConfigCache->project( configFilePath, sServerInterface->serverSettings() );
|
||||
}
|
||||
|
||||
if ( project )
|
||||
|
@ -152,6 +152,17 @@ void QgsServerSettings::initSettings()
|
||||
};
|
||||
mSettings[ sOverrideSystemLocale.envVar ] = sOverrideSystemLocale;
|
||||
|
||||
// bad layers handling
|
||||
const Setting sIgnoreBadLayers = { QgsServerSettingsEnv::QGIS_SERVER_IGNORE_BAD_LAYERS,
|
||||
QgsServerSettingsEnv::DEFAULT_VALUE,
|
||||
QStringLiteral( "Ignore bad layers" ),
|
||||
QString(),
|
||||
QVariant::Bool,
|
||||
QVariant( false ),
|
||||
QVariant()
|
||||
};
|
||||
mSettings[ sIgnoreBadLayers.envVar ] = sIgnoreBadLayers;
|
||||
|
||||
// show group separator
|
||||
const Setting sShowGroupSeparator = { QgsServerSettingsEnv::QGIS_SERVER_SHOW_GROUP_SEPARATOR,
|
||||
QgsServerSettingsEnv::DEFAULT_VALUE,
|
||||
@ -418,3 +429,8 @@ qlonglong QgsServerSettings::apiWfs3MaxLimit() const
|
||||
{
|
||||
return value( QgsServerSettingsEnv::QGIS_SERVER_API_WFS3_MAX_LIMIT ).toLongLong();
|
||||
}
|
||||
|
||||
bool QgsServerSettings::ignoreBadLayers() const
|
||||
{
|
||||
return value( QgsServerSettingsEnv::QGIS_SERVER_IGNORE_BAD_LAYERS ).toBool();
|
||||
}
|
||||
|
@ -59,14 +59,15 @@ class SERVER_EXPORT QgsServerSettingsEnv : public QObject
|
||||
QGIS_SERVER_LOG_STDERR,
|
||||
QGIS_PROJECT_FILE,
|
||||
MAX_CACHE_LAYERS,
|
||||
QGIS_SERVER_IGNORE_BAD_LAYERS, //!< Do not consider the whole project unavailable if it contains bad layers
|
||||
QGIS_SERVER_CACHE_DIRECTORY,
|
||||
QGIS_SERVER_CACHE_SIZE,
|
||||
QGIS_SERVER_SHOW_GROUP_SEPARATOR, //! Show group (thousands) separator when formatting numeric values, defaults to FALSE (since QGIS 3.8)
|
||||
QGIS_SERVER_OVERRIDE_SYSTEM_LOCALE, //! Override system locale (since QGIS 3.8)
|
||||
QGIS_SERVER_WMS_MAX_HEIGHT, //! Maximum height for a WMS request. The most conservative between this and the project one is used (since QGIS 3.6.2)
|
||||
QGIS_SERVER_WMS_MAX_WIDTH, //! Maximum width for a WMS request. The most conservative between this and the project one is used (since QGIS 3.6.2)
|
||||
QGIS_SERVER_API_RESOURCES_DIRECTORY, //! Base directory where HTML templates and static assets (e.g. images, js and css files) are searched for (since QGIS 3.10).
|
||||
QGIS_SERVER_API_WFS3_MAX_LIMIT //! Maximum value for "limit" in a features request, defaults to 10000 (since QGIS 3.10).
|
||||
QGIS_SERVER_SHOW_GROUP_SEPARATOR, //!< Show group (thousands) separator when formatting numeric values, defaults to FALSE (since QGIS 3.8)
|
||||
QGIS_SERVER_OVERRIDE_SYSTEM_LOCALE, //!< Override system locale (since QGIS 3.8)
|
||||
QGIS_SERVER_WMS_MAX_HEIGHT, //!< Maximum height for a WMS request. The most conservative between this and the project one is used (since QGIS 3.6.2)
|
||||
QGIS_SERVER_WMS_MAX_WIDTH, //!< Maximum width for a WMS request. The most conservative between this and the project one is used (since QGIS 3.6.2)
|
||||
QGIS_SERVER_API_RESOURCES_DIRECTORY, //!< Base directory where HTML templates and static assets (e.g. images, js and css files) are searched for (since QGIS 3.10).
|
||||
QGIS_SERVER_API_WFS3_MAX_LIMIT //!< Maximum value for "limit" in a features request, defaults to 10000 (since QGIS 3.10).
|
||||
};
|
||||
Q_ENUM( EnvVar )
|
||||
};
|
||||
@ -222,6 +223,17 @@ class SERVER_EXPORT QgsServerSettings
|
||||
*/
|
||||
qlonglong apiWfs3MaxLimit() const;
|
||||
|
||||
/**
|
||||
* Returns TRUE if the bad layers are ignored and FALSE when the presence of a
|
||||
* bad layers invalidates the whole project making it unavailable.
|
||||
*
|
||||
* The default value is TRUE, this value can be changed by setting the environment
|
||||
* variable QGIS_SERVER_IGNORE_BAD_LAYERS.
|
||||
*
|
||||
* \since QGIS 3.10.5
|
||||
*/
|
||||
bool ignoreBadLayers() const;
|
||||
|
||||
private:
|
||||
void initSettings();
|
||||
QVariant value( QgsServerSettingsEnv::EnvVar envVar ) const;
|
||||
|
@ -341,6 +341,7 @@ IF (WITH_SERVER)
|
||||
ADD_PYTHON_TEST(PyQgsServerWMSGetMap test_qgsserver_wms_getmap.py)
|
||||
ADD_PYTHON_TEST(PyQgsServerWMSGetMapSizeProject test_qgsserver_wms_getmap_size_project.py)
|
||||
ADD_PYTHON_TEST(PyQgsServerWMSGetMapSizeServer test_qgsserver_wms_getmap_size_server.py)
|
||||
ADD_PYTHON_TEST(PyQgsServerWMSGetMapIgnoreBadLayers test_qgsserver_wms_getmap_ignore_bad_layers.py)
|
||||
ADD_PYTHON_TEST(PyQgsServerWMSGetFeatureInfo test_qgsserver_wms_getfeatureinfo.py)
|
||||
ADD_PYTHON_TEST(PyQgsServerWMSGetLegendGraphic test_qgsserver_wms_getlegendgraphic.py)
|
||||
ADD_PYTHON_TEST(PyQgsServerWMSGetPrint test_qgsserver_wms_getprint.py)
|
||||
|
@ -0,0 +1,78 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""QGIS Unit tests for QgsServer WMS GetMap with QGIS_SERVER_IGNORE_BAD_LAYERS=true.
|
||||
|
||||
From build dir, run: ctest -R PyQgsServerWMSGetMap -V
|
||||
|
||||
|
||||
.. note:: This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
"""
|
||||
__author__ = 'Alessandro Pasotti'
|
||||
__date__ = '13/04/2020'
|
||||
__copyright__ = 'Copyright 2020, The QGIS Project'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
import os
|
||||
|
||||
# Needed on Qt 5 so that the serialization of XML is consistent among all executions
|
||||
os.environ['QT_HASH_SEED'] = '1'
|
||||
|
||||
import re
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
import urllib.error
|
||||
|
||||
from qgis.testing import unittest
|
||||
from qgis.PyQt.QtCore import QSize
|
||||
|
||||
import osgeo.gdal # NOQA
|
||||
|
||||
from test_qgsserver import QgsServerTestBase
|
||||
from utilities import unitTestDataPath
|
||||
from qgis.core import QgsProject, QgsApplication
|
||||
|
||||
# Strip path and content length because path may vary
|
||||
RE_STRIP_UNCHECKABLE = b'MAP=[^"]+|Content-Length: \d+'
|
||||
RE_ATTRIBUTES = b'[^>\s]+=[^>\s]+'
|
||||
|
||||
|
||||
class TestQgsServerWMSGetMapIgnoreBadLayers(QgsServerTestBase):
|
||||
"""QGIS Server WMS Tests for GetMap request with QGIS_SERVER_IGNORE_BAD_LAYERS=true"""
|
||||
|
||||
#regenerate_reference = True
|
||||
|
||||
def setUp(self):
|
||||
os.environ['QGIS_SERVER_IGNORE_BAD_LAYERS'] = 'true'
|
||||
super().setUp()
|
||||
|
||||
def test_wms_getmap_datasource_error_ignore(self):
|
||||
"""Must NOT throw a server exception if datasource if not available and QGIS_SERVER_IGNORE_BAD_LAYERS is set"""
|
||||
|
||||
qs = "?" + "&".join(["%s=%s" % i for i in list({
|
||||
"MAP": urllib.parse.quote(os.path.join(self.testdata_path, 'test_project_wms_invalid_layers.qgs')),
|
||||
"SERVICE": "WMS",
|
||||
"VERSION": "1.3.0",
|
||||
"REQUEST": "GetMap",
|
||||
"BBOX": "613402.5658687877003,5809005.018114360981,619594.408781287726,5813869.006602735259",
|
||||
"CRS": "EPSG:25832",
|
||||
"WIDTH": "429",
|
||||
"HEIGHT": "337",
|
||||
"LAYERS": "areas and symbols,osm",
|
||||
"STYLES": ",",
|
||||
"FORMAT": "image/png",
|
||||
"DPI": "200",
|
||||
"MAP_RESOLUTION": "200",
|
||||
"FORMAT_OPTIONS": "dpi:200"
|
||||
}.items())])
|
||||
|
||||
r, h = self._result(self._execute_request(qs))
|
||||
|
||||
self.assertFalse('ServerException' in str(r))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
x
Reference in New Issue
Block a user