From f2cbe042cad1eac4b8c3a39f8eac4f566ca9e59a Mon Sep 17 00:00:00 2001 From: Etienne Trimaille Date: Sat, 28 Mar 2020 08:24:15 +0100 Subject: [PATCH] use the QgsAbstractValidator for validating the project in the server context --- .../metadata/qgslayermetadatavalidator.sip.in | 34 +++++++-- .../qgsprojectservervalidator.sip.in | 19 ++--- src/app/qgsprojectproperties.cpp | 25 ++++++- .../metadata/qgslayermetadatavalidator.cpp | 4 +- src/core/metadata/qgslayermetadatavalidator.h | 35 ++++++--- src/core/qgsprojectservervalidator.cpp | 73 +++++++++++-------- src/core/qgsprojectservervalidator.h | 43 ++++++++--- tests/src/python/CMakeLists.txt | 1 + .../python/test_qgsprojectservervalidator.py | 47 ++++++++++++ 9 files changed, 209 insertions(+), 72 deletions(-) create mode 100644 tests/src/python/test_qgsprojectservervalidator.py diff --git a/python/core/auto_generated/metadata/qgslayermetadatavalidator.sip.in b/python/core/auto_generated/metadata/qgslayermetadatavalidator.sip.in index 353d88fbc234..2b56b1a3c981 100644 --- a/python/core/auto_generated/metadata/qgslayermetadatavalidator.sip.in +++ b/python/core/auto_generated/metadata/qgslayermetadatavalidator.sip.in @@ -10,13 +10,12 @@ - -class QgsAbstractMetadataBaseValidator +class QgsAbstractBaseValidator { %Docstring -Abstract base class for metadata validators. +Abstract base class for validators. -.. versionadded:: 3.0 +.. versionadded:: 3.14 %End %TypeHeaderCode @@ -24,6 +23,9 @@ Abstract base class for metadata validators. %End public: + + virtual ~QgsAbstractBaseValidator(); + struct ValidationResult { @@ -38,10 +40,26 @@ Constructor for ValidationResult. QString note; }; +}; + + + +class QgsAbstractMetadataBaseValidator : QgsAbstractBaseValidator +{ +%Docstring +Abstract base class for metadata validators. + +.. versionadded:: 3.0 +%End + +%TypeHeaderCode +#include "qgslayermetadatavalidator.h" +%End + public: virtual ~QgsAbstractMetadataBaseValidator(); - virtual bool validate( const QgsAbstractMetadataBase *metadata, QList< QgsAbstractMetadataBaseValidator::ValidationResult > &results /Out/ ) const = 0; + virtual bool validate( const QgsAbstractMetadataBase *metadata, QList< QgsAbstractBaseValidator::ValidationResult > &results /Out/ ) const = 0; %Docstring Validates a ``metadata`` object, and returns ``True`` if the metadata is considered valid. @@ -71,7 +89,7 @@ A validator for the native base QGIS metadata schema definition. Constructor for QgsNativeMetadataBaseValidator. %End - virtual bool validate( const QgsAbstractMetadataBase *metadata, QList< QgsAbstractMetadataBaseValidator::ValidationResult > &results /Out/ ) const; + virtual bool validate( const QgsAbstractMetadataBase *metadata, QList< QgsAbstractBaseValidator::ValidationResult > &results /Out/ ) const; }; @@ -96,7 +114,7 @@ A validator for the native QGIS layer metadata schema definition. Constructor for QgsNativeMetadataValidator. %End - virtual bool validate( const QgsAbstractMetadataBase *metadata, QList< QgsAbstractMetadataBaseValidator::ValidationResult > &results /Out/ ) const; + virtual bool validate( const QgsAbstractMetadataBase *metadata, QList< QgsAbstractBaseValidator::ValidationResult > &results /Out/ ) const; }; @@ -120,7 +138,7 @@ A validator for the native QGIS project metadata schema definition. Constructor for QgsNativeProjectMetadataValidator. %End - virtual bool validate( const QgsAbstractMetadataBase *metadata, QList< QgsAbstractMetadataBaseValidator::ValidationResult > &results /Out/ ) const; + virtual bool validate( const QgsAbstractMetadataBase *metadata, QList< QgsAbstractBaseValidator::ValidationResult > &results /Out/ ) const; }; diff --git a/python/core/auto_generated/qgsprojectservervalidator.sip.in b/python/core/auto_generated/qgsprojectservervalidator.sip.in index d6caa6045f26..a9b4a6856b5e 100644 --- a/python/core/auto_generated/qgsprojectservervalidator.sip.in +++ b/python/core/auto_generated/qgsprojectservervalidator.sip.in @@ -7,7 +7,10 @@ ************************************************************************/ -class QgsProjectServerValidator + + + +class QgsProjectServerValidator : QgsAbstractBaseValidator { %Docstring Project server validator. @@ -20,20 +23,12 @@ Project server validator. %End public: - static void checkOWS( QgsLayerTreeGroup *treeGroup, QStringList &owsNames, QStringList &encodingMessages ); + QgsProjectServerValidator(); %Docstring -Recursive function to check a layer tree group. -The function will collect OWS names and encoding for each layers. - -.. versionadded:: 3.14 +Constructor for :py:class:`QgsNativeMetadataBaseValidator`. %End - static QString projectStatusHtml( QgsLayerTree *layerTree ); -%Docstring -Check a layer tree. - -.. versionadded:: 3.14 -%End + bool validate( QgsLayerTree *layerTree, QList< QgsAbstractBaseValidator::ValidationResult > &results /Out/ ) const; }; diff --git a/src/app/qgsprojectproperties.cpp b/src/app/qgsprojectproperties.cpp index de5cbf6312f2..695296aaef7b 100644 --- a/src/app/qgsprojectproperties.cpp +++ b/src/app/qgsprojectproperties.cpp @@ -1906,10 +1906,33 @@ void QgsProjectProperties::pbnWCSLayersDeselectAll_clicked() void QgsProjectProperties::pbnLaunchOWSChecker_clicked() { + QList validationResults; + QgsProjectServerValidator validator; + bool results = validator.validate( QgisApp::instance()->layerTreeView()->layerTreeModel()->rootGroup(), validationResults ); + + QString errors; + if ( !results ) + { + for ( const QgsAbstractBaseValidator::ValidationResult &result : qgis::as_const( validationResults ) ) + { + errors += QLatin1String( "" ) % result.section % QLatin1String( " : " ); + if ( ! result.identifier.isNull() ) + { + errors += QLatin1String( " " ) % result.identifier.toString(); + } + errors += QLatin1String( " : " ) % result.note % QLatin1String( "
" ); + } + } + else + { + errors = QString( tr( "Ok, it seems valid." ) ); + } + QString myStyle = QgsApplication::reportStyleSheet(); + myStyle.append( QStringLiteral( "body { margin: 10px; }\n " ) ); teOWSChecker->clear(); teOWSChecker->document()->setDefaultStyleSheet( myStyle ); - teOWSChecker->setHtml( QgsProjectServerValidator::projectStatusHtml( QgisApp::instance()->layerTreeView()->layerTreeModel()->rootGroup() ) ); + teOWSChecker->setHtml( errors ); } void QgsProjectProperties::pbnAddScale_clicked() diff --git a/src/core/metadata/qgslayermetadatavalidator.cpp b/src/core/metadata/qgslayermetadatavalidator.cpp index 1708fb4438d7..73a404b8c29f 100644 --- a/src/core/metadata/qgslayermetadatavalidator.cpp +++ b/src/core/metadata/qgslayermetadatavalidator.cpp @@ -23,7 +23,7 @@ // QgsNativeMetadataBaseValidator // -bool QgsNativeMetadataBaseValidator::validate( const QgsAbstractMetadataBase *metadata, QList &results ) const +bool QgsNativeMetadataBaseValidator::validate( const QgsAbstractMetadataBase *metadata, QList &results ) const { results.clear(); if ( !metadata ) @@ -185,7 +185,7 @@ bool QgsNativeMetadataValidator::validate( const QgsAbstractMetadataBase *baseMe // QgsNativeProjectMetadataValidator // -bool QgsNativeProjectMetadataValidator::validate( const QgsAbstractMetadataBase *baseMetadata, QList &results ) const +bool QgsNativeProjectMetadataValidator::validate( const QgsAbstractMetadataBase *baseMetadata, QList &results ) const { results.clear(); diff --git a/src/core/metadata/qgslayermetadatavalidator.h b/src/core/metadata/qgslayermetadatavalidator.h index de2e8321d239..02691063b772 100644 --- a/src/core/metadata/qgslayermetadatavalidator.h +++ b/src/core/metadata/qgslayermetadatavalidator.h @@ -28,16 +28,17 @@ class QgsLayerMetadata; /** * \ingroup core - * \class QgsAbstractMetadataBaseValidator - * \brief Abstract base class for metadata validators. - * \since QGIS 3.0 + * \class QgsAbstractBaseValidator + * \brief Abstract base class for validators. + * \since QGIS 3.14 */ - -class CORE_EXPORT QgsAbstractMetadataBaseValidator +class CORE_EXPORT QgsAbstractBaseValidator { - public: + + virtual ~QgsAbstractBaseValidator() = default; + /** * Contains the parameters describing a metadata validation * failure. @@ -68,6 +69,20 @@ class CORE_EXPORT QgsAbstractMetadataBaseValidator //! The reason behind the validation failure. QString note; }; +}; + + +/** + * \ingroup core + * \class QgsAbstractMetadataBaseValidator + * \brief Abstract base class for metadata validators. + * \since QGIS 3.0 + */ + +class CORE_EXPORT QgsAbstractMetadataBaseValidator : public QgsAbstractBaseValidator +{ + + public: virtual ~QgsAbstractMetadataBaseValidator() = default; @@ -78,7 +93,7 @@ class CORE_EXPORT QgsAbstractMetadataBaseValidator * items describing why the validation failed and what needs to be rectified * to fix the metadata. */ - virtual bool validate( const QgsAbstractMetadataBase *metadata, QList< QgsAbstractMetadataBaseValidator::ValidationResult > &results SIP_OUT ) const = 0; + virtual bool validate( const QgsAbstractMetadataBase *metadata, QList< QgsAbstractBaseValidator::ValidationResult > &results SIP_OUT ) const = 0; }; @@ -99,7 +114,7 @@ class CORE_EXPORT QgsNativeMetadataBaseValidator : public QgsAbstractMetadataBas */ QgsNativeMetadataBaseValidator() = default; - bool validate( const QgsAbstractMetadataBase *metadata, QList< QgsAbstractMetadataBaseValidator::ValidationResult > &results SIP_OUT ) const override; + bool validate( const QgsAbstractMetadataBase *metadata, QList< QgsAbstractBaseValidator::ValidationResult > &results SIP_OUT ) const override; }; @@ -121,7 +136,7 @@ class CORE_EXPORT QgsNativeMetadataValidator : public QgsNativeMetadataBaseValid */ QgsNativeMetadataValidator() = default; - bool validate( const QgsAbstractMetadataBase *metadata, QList< QgsAbstractMetadataBaseValidator::ValidationResult > &results SIP_OUT ) const override; + bool validate( const QgsAbstractMetadataBase *metadata, QList< QgsAbstractBaseValidator::ValidationResult > &results SIP_OUT ) const override; }; @@ -142,7 +157,7 @@ class CORE_EXPORT QgsNativeProjectMetadataValidator : public QgsNativeMetadataBa */ QgsNativeProjectMetadataValidator() = default; - bool validate( const QgsAbstractMetadataBase *metadata, QList< QgsAbstractMetadataBaseValidator::ValidationResult > &results SIP_OUT ) const override; + bool validate( const QgsAbstractMetadataBase *metadata, QList< QgsAbstractBaseValidator::ValidationResult > &results SIP_OUT ) const override; }; diff --git a/src/core/qgsprojectservervalidator.cpp b/src/core/qgsprojectservervalidator.cpp index 51e9e96cd76b..a10be3441645 100644 --- a/src/core/qgsprojectservervalidator.cpp +++ b/src/core/qgsprojectservervalidator.cpp @@ -1,10 +1,28 @@ +/*************************************************************************** + qgsprojectservervalidator.cpp + --------------------------- + begin : March 2020 + copyright : (C) 2020 by Etienne Trimaille + email : etienne dot trimaille at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + + #include "qgsapplication.h" #include "qgslayertreelayer.h" #include "qgsprojectservervalidator.h" #include "qgsvectorlayer.h" -void QgsProjectServerValidator::checkOWS( QgsLayerTreeGroup *treeGroup, QStringList &owsNames, QStringList &encodingMessages ) +void QgsProjectServerValidator::browseLayerTree( QgsLayerTreeGroup *treeGroup, QStringList &owsNames, QStringList &encodingMessages ) { QList< QgsLayerTreeNode * > treeGroupChildren = treeGroup->children(); for ( int i = 0; i < treeGroupChildren.size(); ++i ) @@ -18,7 +36,7 @@ void QgsProjectServerValidator::checkOWS( QgsLayerTreeGroup *treeGroup, QStringL owsNames << treeGroupChild->name(); else owsNames << shortName; - checkOWS( treeGroupChild, owsNames, encodingMessages ); + browseLayerTree( treeGroupChild, owsNames, encodingMessages ); } else { @@ -35,7 +53,7 @@ void QgsProjectServerValidator::checkOWS( QgsLayerTreeGroup *treeGroup, QStringL { QgsVectorLayer *vl = static_cast( l ); if ( vl->dataProvider()->encoding() == QLatin1String( "System" ) ) - encodingMessages << QObject::tr( "Update layer \"%1\" encoding" ).arg( l->name() ); + encodingMessages << l->name(); } } } @@ -43,13 +61,16 @@ void QgsProjectServerValidator::checkOWS( QgsLayerTreeGroup *treeGroup, QStringL } -QString QgsProjectServerValidator::projectStatusHtml( QgsLayerTree *layerTree ) +bool QgsProjectServerValidator::validate( QgsLayerTree *layerTree, QList &results ) const { + results.clear(); + bool result = true; - QString html = QStringLiteral( "

" ) + QObject::tr( "Start checking QGIS Server" ) + QStringLiteral( "

" ); + if ( !layerTree ) + return false; QStringList owsNames, encodingMessages; - checkOWS( layerTree, owsNames, encodingMessages ); + browseLayerTree( layerTree, owsNames, encodingMessages ); QStringList duplicateNames, regExpMessages; QRegExp snRegExp = QgsApplication::shortNameRegExp(); @@ -57,46 +78,38 @@ QString QgsProjectServerValidator::projectStatusHtml( QgsLayerTree *layerTree ) for ( const QString &name : constOwsNames ) { if ( !snRegExp.exactMatch( name ) ) - regExpMessages << QObject::tr( "Use short name for \"%1\"" ).arg( name ); + { + regExpMessages << name; + } + if ( duplicateNames.contains( name ) ) + { continue; + } + if ( owsNames.count( name ) > 1 ) + { duplicateNames << name; + } } if ( !duplicateNames.empty() ) { - QString nameMessage = QStringLiteral( "

" ) + QObject::tr( "Some layers and groups have the same name or short name" ) + QStringLiteral( "

" ); - nameMessage += "

" + QObject::tr( "Duplicate names:" ) + "

"; - nameMessage += duplicateNames.join( QStringLiteral( "
  • " ) ) + QStringLiteral( "
  • " ); - html += nameMessage; - } - else - { - html += QStringLiteral( "

    " ) + QObject::tr( "All names and short names of layer and group are unique" ) + QStringLiteral( "

    " ); + result = false; + results << ValidationResult( QObject::tr( "Duplicated names" ), QObject::tr( "Layers/groups have the same name or short name." ), duplicateNames.join( QStringLiteral( ", " ) ) ); } if ( !regExpMessages.empty() ) { - html += QStringLiteral( "

    " ) + QObject::tr( "Some layer short names have to be updated:" ) + QStringLiteral( "

    • " ); - html += regExpMessages.join( QStringLiteral( "
    • " ) ); - html += QStringLiteral( "
    " ); - } - else - { - html += QStringLiteral( "

    " ) + QObject::tr( "All layer short names are well formed" ) + QStringLiteral( "

    " ); + result = false; + results << ValidationResult( QObject::tr( "Short names" ), QObject::tr( "Layers short names have to be updated." ), regExpMessages.join( QStringLiteral( ", " ) ) ); } if ( !encodingMessages.empty() ) { - html += QStringLiteral( "

    " ) + QObject::tr( "Some layer encodings are not set:" ) + QStringLiteral( "

    • " ); - html += encodingMessages.join( QStringLiteral( "
    • " ) ); - html += QStringLiteral( "
    " ); - } - else - { - html += QStringLiteral( "

    " ) + QObject::tr( "All layer encodings are set" ) + QStringLiteral( "

    " ); + result = false; + results << ValidationResult( QObject::tr( "Encoding" ), QObject::tr( "Encoding is not set." ), encodingMessages.join( QStringLiteral( ", " ) ) ); } - return html; + return result; } diff --git a/src/core/qgsprojectservervalidator.h b/src/core/qgsprojectservervalidator.h index 8ed51f877ee8..67b0478c681b 100644 --- a/src/core/qgsprojectservervalidator.h +++ b/src/core/qgsprojectservervalidator.h @@ -1,9 +1,28 @@ +/*************************************************************************** + qgsprojectservervalidator.h + --------------------------- + begin : March 2020 + copyright : (C) 2020 by Etienne Trimaille + email : etienne dot trimaille at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef QGSPROJECTSERVERVALIDATOR_H +#define QGSPROJECTSERVERVALIDATOR_H + #include "qgis_core.h" +#include "qgslayermetadatavalidator.h" #include "qgslayertreegroup.h" #include "qgslayertree.h" -#ifndef QGSPROJECTSERVERVALIDATOR_H -#define QGSPROJECTSERVERVALIDATOR_H /** * \ingroup core @@ -11,23 +30,29 @@ * \brief Project server validator. * \since QGIS 3.14 */ -class CORE_EXPORT QgsProjectServerValidator +class CORE_EXPORT QgsProjectServerValidator : public QgsAbstractBaseValidator { public: /** - * Recursive function to check a layer tree group. - * The function will collect OWS names and encoding for each layers. - * \since QGIS 3.14 + * Constructor for QgsProjectServerValidator. */ - static void checkOWS( QgsLayerTreeGroup *treeGroup, QStringList &owsNames, QStringList &encodingMessages ); + QgsProjectServerValidator() = default; /** - * Check a layer tree. + * Validates a layer tree to avoid some problems on QGIS Server, and returns TRUE if it's considered valid. + * If validation fails, the \a results list will be filled with a list of + * items describing why the validation failed and what needs to be rectified + * \param layerTree input layer tree + * \param results results of the validation + * \returns bool * \since QGIS 3.14 */ - static QString projectStatusHtml( QgsLayerTree *layerTree ); + bool validate( QgsLayerTree *layerTree, QList< QgsAbstractBaseValidator::ValidationResult > &results SIP_OUT ) const; + + private: + static void browseLayerTree( QgsLayerTreeGroup *treeGroup, QStringList &owsNames, QStringList &encodingMessages ); }; diff --git a/tests/src/python/CMakeLists.txt b/tests/src/python/CMakeLists.txt index 32a056c8f48c..8cf5020c3a43 100644 --- a/tests/src/python/CMakeLists.txt +++ b/tests/src/python/CMakeLists.txt @@ -186,6 +186,7 @@ ADD_PYTHON_TEST(PyQgsProcessingAlgDecorator test_processing_alg_decorator.py) ADD_PYTHON_TEST(PyQgsImportIntoPostGIS test_processing_importintopostgis.py) ADD_PYTHON_TEST(PyQgsProjectionSelectionWidgets test_qgsprojectionselectionwidgets.py) ADD_PYTHON_TEST(PyQgsProjectMetadata test_qgsprojectmetadata.py) +ADD_PYTHON_TEST(PyQgsProjectServerValidator test_qgsprojectservervalidator.py) ADD_PYTHON_TEST(PyQgsPropertyOverrideButton test_qgspropertyoverridebutton.py) ADD_PYTHON_TEST(PyQgsProviderConnectionComboBox test_qgsproviderconnectioncombobox.py) ADD_PYTHON_TEST(PyQgsProviderConnectionModel test_qgsproviderconnectionmodel.py) diff --git a/tests/src/python/test_qgsprojectservervalidator.py b/tests/src/python/test_qgsprojectservervalidator.py new file mode 100644 index 000000000000..dabbe4dd3f15 --- /dev/null +++ b/tests/src/python/test_qgsprojectservervalidator.py @@ -0,0 +1,47 @@ +"""QGIS Unit tests for QgsProjectServerValidator + +From build dir, run: +ctest -R PyQgsProjectServerValidator -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__ = 'Etienne Trimaille' +__date__ = '27/03/2020' +__copyright__ = 'Copyright 2020, The QGIS Project' + +from qgis.core import ( + QgsProject, + QgsVectorLayer, + QgsProjectServerValidator, +) +from qgis.testing import start_app, unittest + +app = start_app() + + +class TestQgsprojectServerValidator(unittest.TestCase): + + def test_project_server_validator(self): + """Test project server validator.""" + validator = QgsProjectServerValidator() + project = QgsProject() + layer = QgsVectorLayer("Point?field=fldtxt:string", "layer_1", "memory") + project.addMapLayers([layer]) + + valid, results = validator.validate(project.layerTreeRoot()) + self.assertTrue(valid) + self.assertFalse(results) + + layer_1 = QgsVectorLayer("Point?field=fldtxt:string", "layer_1", "memory") + project.addMapLayers([layer_1]) + + valid, results = validator.validate(project.layerTreeRoot()) + self.assertFalse(valid) + self.assertEqual(results[0].section, 'Duplicated names') + + +if __name__ == '__main__': + unittest.main()