mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-04 00:04:03 -04:00
Merge pull request #63086 from elpaso/server-oapif-configure-rootpath
[server] OAPIF URL root path configuration option
This commit is contained in:
commit
26dd51939c
@ -21,6 +21,7 @@ QgsServerSettingsEnv.QGIS_SERVER_WMS_MAX_HEIGHT = QgsServerSettingsEnv.EnvVar.QG
|
||||
QgsServerSettingsEnv.QGIS_SERVER_WMS_MAX_WIDTH = QgsServerSettingsEnv.EnvVar.QGIS_SERVER_WMS_MAX_WIDTH
|
||||
QgsServerSettingsEnv.QGIS_SERVER_API_RESOURCES_DIRECTORY = QgsServerSettingsEnv.EnvVar.QGIS_SERVER_API_RESOURCES_DIRECTORY
|
||||
QgsServerSettingsEnv.QGIS_SERVER_API_WFS3_MAX_LIMIT = QgsServerSettingsEnv.EnvVar.QGIS_SERVER_API_WFS3_MAX_LIMIT
|
||||
QgsServerSettingsEnv.QGIS_SERVER_API_WFS3_ROOT_PATH = QgsServerSettingsEnv.EnvVar.QGIS_SERVER_API_WFS3_ROOT_PATH
|
||||
QgsServerSettingsEnv.QGIS_SERVER_TRUST_LAYER_METADATA = QgsServerSettingsEnv.EnvVar.QGIS_SERVER_TRUST_LAYER_METADATA
|
||||
QgsServerSettingsEnv.QGIS_SERVER_FORCE_READONLY_LAYERS = QgsServerSettingsEnv.EnvVar.QGIS_SERVER_FORCE_READONLY_LAYERS
|
||||
QgsServerSettingsEnv.QGIS_SERVER_DISABLE_GETPRINT = QgsServerSettingsEnv.EnvVar.QGIS_SERVER_DISABLE_GETPRINT
|
||||
|
@ -49,6 +49,7 @@ configuration.
|
||||
QGIS_SERVER_WMS_MAX_WIDTH,
|
||||
QGIS_SERVER_API_RESOURCES_DIRECTORY,
|
||||
QGIS_SERVER_API_WFS3_MAX_LIMIT,
|
||||
QGIS_SERVER_API_WFS3_ROOT_PATH,
|
||||
QGIS_SERVER_TRUST_LAYER_METADATA,
|
||||
QGIS_SERVER_FORCE_READONLY_LAYERS,
|
||||
QGIS_SERVER_DISABLE_GETPRINT,
|
||||
@ -272,6 +273,20 @@ 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
|
||||
|
||||
QString apiWfs3RootPath() const;
|
||||
%Docstring
|
||||
Returns the server-wide root path for OAPIF (WFS3) service API.
|
||||
|
||||
The default value is "/wfs3", this value can be changed by setting the
|
||||
environment variable QGIS_SERVER_API_WFS3_ROOT_PATH.
|
||||
|
||||
.. note::
|
||||
|
||||
The default will be changed to "/ogcapi" for QGIS 4.
|
||||
|
||||
.. versionadded:: 3.44.3
|
||||
%End
|
||||
|
||||
bool ignoreBadLayers() const;
|
||||
|
@ -49,6 +49,7 @@ configuration.
|
||||
QGIS_SERVER_WMS_MAX_WIDTH,
|
||||
QGIS_SERVER_API_RESOURCES_DIRECTORY,
|
||||
QGIS_SERVER_API_WFS3_MAX_LIMIT,
|
||||
QGIS_SERVER_API_WFS3_ROOT_PATH,
|
||||
QGIS_SERVER_TRUST_LAYER_METADATA,
|
||||
QGIS_SERVER_FORCE_READONLY_LAYERS,
|
||||
QGIS_SERVER_DISABLE_GETPRINT,
|
||||
@ -272,6 +273,20 @@ 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
|
||||
|
||||
QString apiWfs3RootPath() const;
|
||||
%Docstring
|
||||
Returns the server-wide root path for OAPIF (WFS3) service API.
|
||||
|
||||
The default value is "/wfs3", this value can be changed by setting the
|
||||
environment variable QGIS_SERVER_API_WFS3_ROOT_PATH.
|
||||
|
||||
.. note::
|
||||
|
||||
The default will be changed to "/ogcapi" for QGIS 4.
|
||||
|
||||
.. versionadded:: 3.44.3
|
||||
%End
|
||||
|
||||
bool ignoreBadLayers() const;
|
||||
|
@ -119,6 +119,16 @@ void QgsServerSettings::initSettings()
|
||||
|
||||
mSettings[sApiWfs3MaxLimit.envVar] = sApiWfs3MaxLimit;
|
||||
|
||||
// API WFS3 root path
|
||||
// TODO: remove when QGIS 4 is released
|
||||
#if _QGIS_VERSION_INT > 40000
|
||||
const Setting sApiWfs3RootPath = { QgsServerSettingsEnv::QGIS_SERVER_API_WFS3_ROOT_PATH, QgsServerSettingsEnv::DEFAULT_VALUE, QStringLiteral( "Root path for the OAPIF (WFS3) API" ), QStringLiteral( "/qgis/server_api_wfs3_root_path" ), QMetaType::Type::QString, QVariant( "/ogcapi" ), QVariant() };
|
||||
#else
|
||||
const Setting sApiWfs3RootPath = { QgsServerSettingsEnv::QGIS_SERVER_API_WFS3_ROOT_PATH, QgsServerSettingsEnv::DEFAULT_VALUE, QStringLiteral( "Root path for the OAPIF (WFS3) API" ), QStringLiteral( "/qgis/server_api_wfs3_root_path" ), QMetaType::Type::QString, QVariant( "/wfs3" ), QVariant() };
|
||||
#endif
|
||||
|
||||
mSettings[sApiWfs3RootPath.envVar] = sApiWfs3RootPath;
|
||||
|
||||
// projects directory for landing page service
|
||||
const Setting sProjectsDirectories = { QgsServerSettingsEnv::QGIS_SERVER_LANDING_PAGE_PROJECTS_DIRECTORIES, QgsServerSettingsEnv::DEFAULT_VALUE, QStringLiteral( "Directories used by the landing page service to find .qgs and .qgz projects" ), QStringLiteral( "/qgis/server_projects_directories" ), QMetaType::Type::QString, QVariant( "" ), QVariant() };
|
||||
|
||||
@ -414,6 +424,11 @@ qlonglong QgsServerSettings::apiWfs3MaxLimit() const
|
||||
return value( QgsServerSettingsEnv::QGIS_SERVER_API_WFS3_MAX_LIMIT ).toLongLong();
|
||||
}
|
||||
|
||||
QString QgsServerSettings::apiWfs3RootPath() const
|
||||
{
|
||||
return value( QgsServerSettingsEnv::QGIS_SERVER_API_WFS3_ROOT_PATH ).toString();
|
||||
}
|
||||
|
||||
bool QgsServerSettings::ignoreBadLayers() const
|
||||
{
|
||||
return value( QgsServerSettingsEnv::QGIS_SERVER_IGNORE_BAD_LAYERS ).toBool();
|
||||
|
@ -67,6 +67,7 @@ class SERVER_EXPORT QgsServerSettingsEnv : public QObject
|
||||
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_API_WFS3_ROOT_PATH, //!< Root path for OAPIF (WFS3) service API, default value is "/wfs3" \note Default will be changed to "/ogcapi" for QGIS 4. \since QGIS 3.44.3
|
||||
QGIS_SERVER_TRUST_LAYER_METADATA, //!< Trust layer metadata. Improves project read time. \since QGIS 3.16
|
||||
QGIS_SERVER_FORCE_READONLY_LAYERS, //!< Force to open layers in read-only mode. \since QGIS 3.28
|
||||
QGIS_SERVER_DISABLE_GETPRINT, //!< Disabled WMS GetPrint request and don't load layouts. Improves project read time. \since QGIS 3.16
|
||||
@ -263,6 +264,16 @@ class SERVER_EXPORT QgsServerSettings
|
||||
*/
|
||||
qlonglong apiWfs3MaxLimit() const;
|
||||
|
||||
/**
|
||||
* Returns the server-wide root path for OAPIF (WFS3) service API.
|
||||
*
|
||||
* The default value is "/wfs3", this value can be changed by setting the environment
|
||||
* variable QGIS_SERVER_API_WFS3_ROOT_PATH.
|
||||
* \note The default will be changed to "/ogcapi" for QGIS 4.
|
||||
* \since QGIS 3.44.3
|
||||
*/
|
||||
QString apiWfs3RootPath() 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.
|
||||
|
@ -353,7 +353,7 @@ bool QgsServiceRegistry::registerApi( QgsServerApi *api )
|
||||
return false;
|
||||
}
|
||||
|
||||
QgsMessageLog::logMessage( QStringLiteral( "Adding API %1 %2" ).arg( name, version ), QString(), Qgis::MessageLevel::Info );
|
||||
QgsMessageLog::logMessage( QStringLiteral( "Adding API %1 %2 - root path: %3" ).arg( name, version, api->rootPath() ), QString(), Qgis::MessageLevel::Info );
|
||||
mApis.insert( key, std::shared_ptr<QgsServerApi>( api ) );
|
||||
|
||||
// Check the default version
|
||||
|
@ -23,7 +23,7 @@
|
||||
/**
|
||||
* \ingroup server
|
||||
* \class QgsWfsModule
|
||||
* \brief Module specialized for WFS3 service
|
||||
* \brief Module specialized for OAPIF (WFS3) service
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
class QgsWfs3Module : public QgsServiceModule
|
||||
@ -31,7 +31,17 @@ class QgsWfs3Module : public QgsServiceModule
|
||||
public:
|
||||
void registerSelf( QgsServiceRegistry ®istry, QgsServerInterface *serverIface ) override
|
||||
{
|
||||
QgsServerOgcApi *wfs3Api = new QgsServerOgcApi { serverIface, QStringLiteral( "/wfs3" ), QStringLiteral( "OGC WFS3 (Draft)" ), QStringLiteral( "1.0.0" ) };
|
||||
// TODO: remove when QGIS 4 is released
|
||||
#if _QGIS_VERSION_INT >= 40000
|
||||
QString rootPath = QStringLiteral( "/ogcapi" );
|
||||
#else
|
||||
QString rootPath = QStringLiteral( "/wfs3" );
|
||||
#endif
|
||||
if ( serverIface && serverIface->serverSettings() && !serverIface->serverSettings()->apiWfs3RootPath().isEmpty() )
|
||||
{
|
||||
rootPath = serverIface->serverSettings()->apiWfs3RootPath();
|
||||
}
|
||||
std::unique_ptr<QgsServerOgcApi> wfs3Api = std::make_unique<QgsServerOgcApi>( serverIface, rootPath, QStringLiteral( "OAPIF" ), QStringLiteral( "1.0.0" ) );
|
||||
// Register handlers
|
||||
wfs3Api->registerHandler<QgsWfs3CollectionsItemsHandler>();
|
||||
wfs3Api->registerHandler<QgsWfs3CollectionsFeatureHandler>();
|
||||
@ -40,11 +50,11 @@ class QgsWfs3Module : public QgsServiceModule
|
||||
wfs3Api->registerHandler<QgsWfs3ConformanceHandler>();
|
||||
wfs3Api->registerHandler<QgsServerStaticHandler>();
|
||||
// API handler must access to the whole API
|
||||
wfs3Api->registerHandler<QgsWfs3APIHandler>( wfs3Api );
|
||||
wfs3Api->registerHandler<QgsWfs3APIHandler>( wfs3Api.get() );
|
||||
wfs3Api->registerHandler<QgsWfs3LandingPageHandler>();
|
||||
|
||||
// Register API
|
||||
registry.registerApi( wfs3Api );
|
||||
registry.registerApi( wfs3Api.release() );
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -213,6 +213,17 @@ QgsFields QgsWfs3AbstractItemsHandler::publishedFields( const QgsVectorLayer *vL
|
||||
return publishedFields;
|
||||
}
|
||||
|
||||
const QString QgsWfs3AbstractItemsHandler::templatePath( const QgsServerApiContext &context ) const
|
||||
{
|
||||
// resources/server/api + /ogc/templates/ + operationId + .html
|
||||
QString path { context.serverInterface()->serverSettings()->apiResourcesDirectory() };
|
||||
path += QLatin1String( "/ogc/templates/wfs3" );
|
||||
path += '/';
|
||||
path += QString::fromStdString( operationId() );
|
||||
path += QLatin1String( ".html" );
|
||||
return path;
|
||||
}
|
||||
|
||||
QgsWfs3LandingPageHandler::QgsWfs3LandingPageHandler()
|
||||
{
|
||||
}
|
||||
|
@ -59,6 +59,16 @@ class QgsWfs3AbstractItemsHandler : public QgsServerOgcApiHandler
|
||||
* \return QgsFields list with filters applied
|
||||
*/
|
||||
QgsFields publishedFields( const QgsVectorLayer *layer, const QgsServerApiContext &context ) const;
|
||||
|
||||
/**
|
||||
* Returns the HTML template path for the handler in the given \a context
|
||||
*
|
||||
* The template path is calculated from QgsServerSettings's apiResourcesDirectory() as follow:
|
||||
* apiResourcesDirectory() + "/ogc/templates/wfs3/" + operationId + ".html"
|
||||
* e.g. for an handler with operationId "collectionItems", the path
|
||||
* will be apiResourcesDirectory() + "/ogc/templates/wfs3/collectionItems.html"
|
||||
*/
|
||||
const QString templatePath( const QgsServerApiContext &context ) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -24,12 +24,14 @@ os.environ["QT_HASH_SEED"] = "1"
|
||||
from urllib import parse
|
||||
|
||||
from qgis.core import (
|
||||
Qgis,
|
||||
QgsFeature,
|
||||
QgsFeatureRequest,
|
||||
QgsGeometry,
|
||||
QgsProject,
|
||||
QgsVectorLayer,
|
||||
QgsVectorLayerServerProperties,
|
||||
QgsApplication,
|
||||
)
|
||||
from qgis.PyQt import QtCore
|
||||
from qgis.server import (
|
||||
@ -49,6 +51,7 @@ from qgis.server import (
|
||||
from qgis.testing import unittest
|
||||
from test_qgsserver import QgsServerTestBase
|
||||
from utilities import unitTestDataPath
|
||||
from contextlib import contextmanager
|
||||
|
||||
|
||||
class QgsServerAPIUtilsTest(QgsServerTestBase):
|
||||
@ -283,7 +286,9 @@ class QgsServerAPITestBase(QgsServerTestBase):
|
||||
)
|
||||
return headers_content + "\n" + json_content
|
||||
|
||||
def compareApi(self, request, project, reference_file, subdir="api"):
|
||||
def compareApi(
|
||||
self, request, project, reference_file, subdir="api", replace_map=None
|
||||
):
|
||||
response = QgsBufferServerResponse()
|
||||
# Add json to accept it reference_file is JSON
|
||||
if reference_file.endswith(".json"):
|
||||
@ -294,6 +299,11 @@ class QgsServerAPITestBase(QgsServerTestBase):
|
||||
if reference_file.endswith("html")
|
||||
else self.dump(response)
|
||||
)
|
||||
|
||||
if replace_map:
|
||||
for k, v in replace_map.items():
|
||||
result = result.replace(k, v)
|
||||
|
||||
path = os.path.join(self.temporary_path, "qgis_server", subdir, reference_file)
|
||||
if self.regeregenerate_api_reference:
|
||||
# Try to change timestamp
|
||||
@ -374,6 +384,39 @@ class RestrictedLayerAccessControl(QgsAccessControlFilter):
|
||||
class QgsServerAPITest(QgsServerAPITestBase):
|
||||
"""QGIS API server tests"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
# TODO: Remove when QGIS 4 is released
|
||||
# Default url will change when QGIS 4 is released, set it to /wfs3 for now
|
||||
if Qgis.versionInt() >= 40000:
|
||||
os.environ.update({"QGIS_SERVER_API_WFS3_ROOT_PATH": "/wfs3"})
|
||||
iface = self.server.serverInterface()
|
||||
iface.reloadSettings()
|
||||
iface.serviceRegistry().cleanUp()
|
||||
iface.serviceRegistry().init(QgsApplication.libexecPath() + "server", iface)
|
||||
|
||||
# Set env context manager
|
||||
@contextmanager
|
||||
def set_env(self, **environ):
|
||||
"""Context manager to set/unset environment variables"""
|
||||
|
||||
old_environ = dict(os.environ)
|
||||
os.environ.update(environ)
|
||||
|
||||
iface = self.server.serverInterface()
|
||||
iface.reloadSettings()
|
||||
iface.serviceRegistry().cleanUp()
|
||||
iface.serviceRegistry().init(QgsApplication.libexecPath() + "server", iface)
|
||||
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
os.environ.clear()
|
||||
os.environ.update(old_environ)
|
||||
iface.reloadSettings()
|
||||
iface.serviceRegistry().cleanUp()
|
||||
iface.serviceRegistry().init(QgsApplication.libexecPath() + "server", iface)
|
||||
|
||||
def test_api(self):
|
||||
"""Test API registering"""
|
||||
|
||||
@ -522,6 +565,13 @@ class QgsServerAPITest(QgsServerAPITestBase):
|
||||
)
|
||||
self.compareApi(request, project, "test_wfs3_api_project.json")
|
||||
|
||||
with self.set_env(QGIS_SERVER_API_WFS3_ROOT_PATH="/custom_rootpath"):
|
||||
iface = self.server.serverInterface()
|
||||
registry = iface.serviceRegistry()
|
||||
api = registry.getApi("OAPIF")
|
||||
self.assertIsNotNone(api)
|
||||
self.assertEqual(api.rootPath(), "/custom_rootpath")
|
||||
|
||||
def test_wfs3_api_permissions(self):
|
||||
"""Test the API with different permissions on a layer"""
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user