[FEATURE][processing] Add a new parameter type for authentication config

This adds a new available parameter type for processing algorithms,
QgsProcessingParameterAuthConfig, allowing selection from available
authentication configurations (and creation of new ones).

It allows creation of processing algorithm which can fully take
advantage of QGIS' mature authentication handling, avoiding the
need to use insecure string parameters for users to input
sensitive logon credentials.

QgsProcessingParameterAuthConfig parameters are evaluated using
QgsProcessingAlgorithm.parameterAsString(), which returns the
selected authentication configuration ID.
This commit is contained in:
Nyall Dawson 2018-12-13 12:43:32 +10:00
parent 8f06b5a996
commit 11ea28a5b3
14 changed files with 519 additions and 15 deletions

View File

@ -161,6 +161,8 @@ their acceptable ranges, defaults, etc.
sipType = sipType_QgsProcessingParameterString;
else if ( sipCpp->type() == QgsProcessingParameterExpression::typeName() )
sipType = sipType_QgsProcessingParameterExpression;
else if ( sipCpp->type() == QgsProcessingParameterAuthConfig::typeName() )
sipType = sipType_QgsProcessingParameterAuthConfig;
else if ( sipCpp->type() == QgsProcessingParameterVectorLayer::typeName() )
sipType = sipType_QgsProcessingParameterVectorLayer;
else if ( sipCpp->type() == QgsProcessingParameterField::typeName() )
@ -1767,6 +1769,50 @@ Creates a new parameter using the definition from a script code.
};
class QgsProcessingParameterAuthConfig : QgsProcessingParameterDefinition
{
%Docstring
A string parameter for authentication configuration configuration ID values.
This parameter allows for users to select from available authentication configurations,
or create new authentication configurations as required.
QgsProcessingParameterAuthConfig should be evaluated by calling :py:func:`QgsProcessingAlgorithm.parameterAsString()`
.. versionadded:: 3.6
%End
%TypeHeaderCode
#include "qgsprocessingparameters.h"
%End
public:
QgsProcessingParameterAuthConfig( const QString &name, const QString &description = QString(), const QVariant &defaultValue = QVariant(),
bool optional = false );
%Docstring
Constructor for QgsProcessingParameterAuthConfig.
%End
static QString typeName();
%Docstring
Returns the type name for the parameter class.
%End
virtual QgsProcessingParameterDefinition *clone() const /Factory/;
virtual QString type() const;
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
virtual QString asScriptCode() const;
static QgsProcessingParameterAuthConfig *fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition ) /Factory/;
%Docstring
Creates a new parameter using the definition from a script code.
%End
};
class QgsProcessingParameterExpression : QgsProcessingParameterDefinition
{
%Docstring

View File

@ -31,6 +31,7 @@ from qgis.PyQt.QtGui import QIcon
from qgis.core import (QgsProcessingParameterDefinition,
QgsProcessingAlgorithm,
QgsProcessingParameterString,
QgsProcessingParameterAuthConfig,
QgsProcessingParameterNumber,
QgsProcessingParameterDistance,
QgsProcessingParameterFeatureSource,
@ -222,7 +223,7 @@ class AlgWrapper(QgsProcessingAlgorithm):
"""
Extract the real value from the parameter.
"""
if isinstance(parm, QgsProcessingParameterString):
if isinstance(parm, (QgsProcessingParameterString, QgsProcessingParameterAuthConfig)):
value = self.parameterAsString(parameters, name, context)
return value
elif isinstance(parm, QgsProcessingParameterNumber):
@ -312,6 +313,7 @@ class ProcessingAlgFactory():
MATRIX = "MATRIX",
POINT = "POINT",
RANGE = "RANGE",
AUTH_CFG = "AUTH_CFG"
def __init__(self):
self._current = None
@ -436,6 +438,7 @@ class ProcessingAlgFactory():
alg.POINT: QgsProcessingParameterPoint
alg.RANGE: QgsProcessingParameterRange
alg.VECTOR_LAYER: QgsProcessingParameterVectorLayer
alg.AUTH_CFG: QgsProcessingParameterAuthConfig
:param type: The type of the input. This should be a type define on `alg` like alg.STRING, alg.DISTANCE
@ -481,6 +484,7 @@ input_type_mapping = {
ProcessingAlgFactory.POINT: QgsProcessingParameterPoint,
ProcessingAlgFactory.RANGE: QgsProcessingParameterRange,
ProcessingAlgFactory.VECTOR_LAYER: QgsProcessingParameterVectorLayer,
ProcessingAlgFactory.AUTH_CFG: QgsProcessingParameterAuthConfig,
}
output_type_mapping = {

View File

@ -452,7 +452,8 @@ QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> QgsProcessingMode
<< QgsProcessingParameterBoolean::typeName()
<< QgsProcessingParameterExpression::typeName()
<< QgsProcessingParameterField::typeName()
<< QgsProcessingParameterString::typeName(),
<< QgsProcessingParameterString::typeName()
<< QgsProcessingParameterAuthConfig::typeName(),
QStringList() << QgsProcessingOutputNumber::typeName()
<< QgsProcessingOutputString::typeName() );
Q_FOREACH ( const QgsProcessingModelChildParameterSource &source, sources )

View File

@ -1463,6 +1463,8 @@ QgsProcessingParameterDefinition *QgsProcessingParameters::parameterFromVariantM
def.reset( new QgsProcessingParameterEnum( name ) );
else if ( type == QgsProcessingParameterString::typeName() )
def.reset( new QgsProcessingParameterString( name ) );
else if ( type == QgsProcessingParameterAuthConfig::typeName() )
def.reset( new QgsProcessingParameterAuthConfig( name ) );
else if ( type == QgsProcessingParameterExpression::typeName() )
def.reset( new QgsProcessingParameterExpression( name ) );
else if ( type == QgsProcessingParameterVectorLayer::typeName() )
@ -1543,6 +1545,8 @@ QgsProcessingParameterDefinition *QgsProcessingParameters::parameterFromScriptCo
return QgsProcessingParameterEnum::fromScriptCode( name, description, isOptional, definition );
else if ( type == QStringLiteral( "string" ) )
return QgsProcessingParameterString::fromScriptCode( name, description, isOptional, definition );
else if ( type == QStringLiteral( "authcfg" ) )
return QgsProcessingParameterAuthConfig::fromScriptCode( name, description, isOptional, definition );
else if ( type == QStringLiteral( "expression" ) )
return QgsProcessingParameterExpression::fromScriptCode( name, description, isOptional, definition );
else if ( type == QStringLiteral( "field" ) )
@ -3008,6 +3012,62 @@ QgsProcessingParameterString *QgsProcessingParameterString::fromScriptCode( cons
return new QgsProcessingParameterString( name, description, defaultValue, multiLine, isOptional );
}
//
// QgsProcessingParameterAuthConfig
//
QgsProcessingParameterAuthConfig::QgsProcessingParameterAuthConfig( const QString &name, const QString &description, const QVariant &defaultValue, bool optional )
: QgsProcessingParameterDefinition( name, description, defaultValue, optional )
{
}
QgsProcessingParameterDefinition *QgsProcessingParameterAuthConfig::clone() const
{
return new QgsProcessingParameterAuthConfig( *this );
}
QString QgsProcessingParameterAuthConfig::valueAsPythonString( const QVariant &value, QgsProcessingContext & ) const
{
if ( !value.isValid() )
return QStringLiteral( "None" );
QString s = value.toString();
return QgsProcessingUtils::stringToPythonLiteral( s );
}
QString QgsProcessingParameterAuthConfig::asScriptCode() const
{
QString code = QStringLiteral( "##%1=" ).arg( mName );
if ( mFlags & FlagOptional )
code += QStringLiteral( "optional " );
code += QStringLiteral( "authcfg " );
code += mDefault.toString();
return code.trimmed();
}
QgsProcessingParameterAuthConfig *QgsProcessingParameterAuthConfig::fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition )
{
QString def = definition;
if ( def.startsWith( '"' ) || def.startsWith( '\'' ) )
def = def.mid( 1 );
if ( def.endsWith( '"' ) || def.endsWith( '\'' ) )
def.chop( 1 );
QVariant defaultValue = def;
if ( def == QStringLiteral( "None" ) )
defaultValue = QVariant();
return new QgsProcessingParameterAuthConfig( name, description, defaultValue, isOptional );
}
//
// QgsProcessingParameterExpression
//
QgsProcessingParameterExpression::QgsProcessingParameterExpression( const QString &name, const QString &description, const QVariant &defaultValue, const QString &parentLayerParameterName, bool optional )
: QgsProcessingParameterDefinition( name, description, defaultValue, optional )
, mParentLayerParameterName( parentLayerParameterName )

View File

@ -1,10 +1,10 @@
/***************************************************************************
qgsprocessingparameters.h
-------------------------
begin : April 2017
copyright : (C) 2017 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
qgsprocessingparameters.h
-------------------------
begin : April 2017
copyright : (C) 2017 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
@ -234,6 +234,8 @@ class CORE_EXPORT QgsProcessingParameterDefinition
sipType = sipType_QgsProcessingParameterString;
else if ( sipCpp->type() == QgsProcessingParameterExpression::typeName() )
sipType = sipType_QgsProcessingParameterExpression;
else if ( sipCpp->type() == QgsProcessingParameterAuthConfig::typeName() )
sipType = sipType_QgsProcessingParameterAuthConfig;
else if ( sipCpp->type() == QgsProcessingParameterVectorLayer::typeName() )
sipType = sipType_QgsProcessingParameterVectorLayer;
else if ( sipCpp->type() == QgsProcessingParameterField::typeName() )
@ -1723,6 +1725,45 @@ class CORE_EXPORT QgsProcessingParameterString : public QgsProcessingParameterDe
};
/**
* \class QgsProcessingParameterAuthConfig
* \ingroup core
* A string parameter for authentication configuration configuration ID values.
*
* This parameter allows for users to select from available authentication configurations,
* or create new authentication configurations as required.
*
* QgsProcessingParameterAuthConfig should be evaluated by calling QgsProcessingAlgorithm::parameterAsString().
*
* \since QGIS 3.6
*/
class CORE_EXPORT QgsProcessingParameterAuthConfig : public QgsProcessingParameterDefinition
{
public:
/**
* Constructor for QgsProcessingParameterAuthConfig.
*/
QgsProcessingParameterAuthConfig( const QString &name, const QString &description = QString(), const QVariant &defaultValue = QVariant(),
bool optional = false );
/**
* Returns the type name for the parameter class.
*/
static QString typeName() { return QStringLiteral( "authcfg" ); }
QgsProcessingParameterDefinition *clone() const override SIP_FACTORY;
QString type() const override { return typeName(); }
QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override;
QString asScriptCode() const override;
/**
* Creates a new parameter using the definition from a script code.
*/
static QgsProcessingParameterAuthConfig *fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition ) SIP_FACTORY;
};
/**
* \class QgsProcessingParameterExpression
* \ingroup core

View File

@ -744,6 +744,41 @@ class CORE_EXPORT QgsProcessingParameterTypeString : public QgsProcessingParamet
}
};
/**
* A authentication configuration parameter for processing algorithms.
*
* \ingroup core
* \note No Python bindings available. Get your copy from QgsApplication.processingRegistry().parameterType('authcfg')
* \since QGIS 3.6
*/
class CORE_EXPORT QgsProcessingParameterTypeAuthConfig : public QgsProcessingParameterType
{
QgsProcessingParameterDefinition *create( const QString &name ) const override SIP_FACTORY
{
return new QgsProcessingParameterAuthConfig( name );
}
QString description() const override
{
return QCoreApplication::translate( "Processing", "A authentication configuration parameter." );
}
QString name() const override
{
return QCoreApplication::translate( "Processing", "Authentication Configuration" );
}
QString id() const override
{
return QStringLiteral( "authcfg" );
}
QStringList acceptedPythonTypes() const override
{
return QStringList() << QStringLiteral( "str" );
}
};
/**
* A parameter for processing algorithms which accepts multiple map layers.
*

View File

@ -40,6 +40,7 @@ QgsProcessingRegistry::QgsProcessingRegistry( QObject *parent SIP_TRANSFERTHIS )
addParameterType( new QgsProcessingParameterTypeFileDestination() );
addParameterType( new QgsProcessingParameterTypeFolderDestination() );
addParameterType( new QgsProcessingParameterTypeString() );
addParameterType( new QgsProcessingParameterTypeAuthConfig() );
addParameterType( new QgsProcessingParameterTypeMultipleLayers() );
addParameterType( new QgsProcessingParameterTypeFeatureSource() );
addParameterType( new QgsProcessingParameterTypeNumber() );

View File

@ -33,6 +33,7 @@ QgsProcessingGuiRegistry::QgsProcessingGuiRegistry()
addParameterWidgetFactory( new QgsProcessingNumericWidgetWrapper() );
addParameterWidgetFactory( new QgsProcessingDistanceWidgetWrapper() );
addParameterWidgetFactory( new QgsProcessingRangeWidgetWrapper() );
addParameterWidgetFactory( new QgsProcessingAuthConfigWidgetWrapper() );
}
QgsProcessingGuiRegistry::~QgsProcessingGuiRegistry()

View File

@ -22,6 +22,7 @@
#include "qgsspinbox.h"
#include "qgsdoublespinbox.h"
#include "qgsprocessingcontext.h"
#include "qgsauthconfigselect.h"
#include <QLabel>
#include <QHBoxLayout>
#include <QCheckBox>
@ -376,6 +377,7 @@ QStringList QgsProcessingStringWidgetWrapper::compatibleParameterTypes() const
{
return QStringList()
<< QgsProcessingParameterString::typeName()
<< QgsProcessingParameterAuthConfig::typeName()
<< QgsProcessingParameterNumber::typeName()
<< QgsProcessingParameterDistance::typeName()
<< QgsProcessingParameterFile::typeName()
@ -407,6 +409,80 @@ QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingStringWidgetWrapper::c
//
// QgsProcessingAuthConfigWidgetWrapper
//
QgsProcessingAuthConfigWidgetWrapper::QgsProcessingAuthConfigWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type, QWidget *parent )
: QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
{
}
QWidget *QgsProcessingAuthConfigWidgetWrapper::createWidget()
{
switch ( type() )
{
case QgsProcessingGui::Standard:
case QgsProcessingGui::Modeler:
case QgsProcessingGui::Batch:
{
mAuthConfigSelect = new QgsAuthConfigSelect();
mAuthConfigSelect->setToolTip( parameterDefinition()->toolTip() );
connect( mAuthConfigSelect, &QgsAuthConfigSelect::selectedConfigIdChanged, this, [ = ]
{
emit widgetValueHasChanged( this );
} );
return mAuthConfigSelect;
};
}
return nullptr;
}
void QgsProcessingAuthConfigWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
{
const QString v = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
if ( mAuthConfigSelect )
mAuthConfigSelect->setConfigId( v );
}
QVariant QgsProcessingAuthConfigWidgetWrapper::widgetValue() const
{
if ( mAuthConfigSelect )
return mAuthConfigSelect->configId();
else
return QVariant();
}
QStringList QgsProcessingAuthConfigWidgetWrapper::compatibleParameterTypes() const
{
return QStringList()
<< QgsProcessingParameterAuthConfig::typeName()
<< QgsProcessingParameterString::typeName()
<< QgsProcessingParameterExpression::typeName();
}
QStringList QgsProcessingAuthConfigWidgetWrapper::compatibleOutputTypes() const
{
return QStringList() << QgsProcessingOutputString::typeName();
}
QList<int> QgsProcessingAuthConfigWidgetWrapper::compatibleDataTypes() const
{
return QList< int >();
}
QString QgsProcessingAuthConfigWidgetWrapper::parameterType() const
{
return QgsProcessingParameterAuthConfig::typeName();
}
QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingAuthConfigWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type )
{
return new QgsProcessingAuthConfigWidgetWrapper( parameter, type );
}
//
// QgsProcessingNumericWidgetWrapper
//

View File

@ -29,7 +29,7 @@ class QPlainTextEdit;
class QgsProjectionSelectionWidget;
class QgsSpinBox;
class QgsDoubleSpinBox;
class QgsAuthConfigSelect;
///@cond PRIVATE
@ -138,6 +138,42 @@ class GUI_EXPORT QgsProcessingStringWidgetWrapper : public QgsAbstractProcessing
friend class TestProcessingGui;
};
class GUI_EXPORT QgsProcessingAuthConfigWidgetWrapper : public QgsAbstractProcessingParameterWidgetWrapper, public QgsProcessingParameterWidgetFactoryInterface
{
Q_OBJECT
public:
QgsProcessingAuthConfigWidgetWrapper( const QgsProcessingParameterDefinition *parameter = nullptr,
QgsProcessingGui::WidgetType type = QgsProcessingGui::Standard, QWidget *parent = nullptr );
// QgsProcessingParameterWidgetFactoryInterface
QString parameterType() const override;
QgsAbstractProcessingParameterWidgetWrapper *createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type ) override;
// QgsProcessingParameterWidgetWrapper interface
QWidget *createWidget() override SIP_FACTORY;
protected:
void setWidgetValue( const QVariant &value, QgsProcessingContext &context ) override;
QVariant widgetValue() const override;
QStringList compatibleParameterTypes() const override;
QStringList compatibleOutputTypes() const override;
QList< int > compatibleDataTypes() const override;
private:
QgsAuthConfigSelect *mAuthConfigSelect = nullptr;
friend class TestProcessingGui;
};
class GUI_EXPORT QgsProcessingNumericWidgetWrapper : public QgsAbstractProcessingParameterWidgetWrapper, public QgsProcessingParameterWidgetFactoryInterface
{
Q_OBJECT

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>452</width>
<height>96</height>
<height>79</height>
</rect>
</property>
<property name="windowTitle">
@ -15,16 +15,16 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>6</number>
<number>0</number>
</property>
<property name="topMargin">
<number>6</number>
<number>0</number>
</property>
<property name="rightMargin">
<number>6</number>
<number>0</number>
</property>
<property name="bottomMargin">
<number>6</number>
<number>0</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout">
@ -129,7 +129,7 @@
</layout>
</widget>
</item>
<item row="1" column="3">
<item row="1" column="3" colspan="2">
<widget class="QToolButton" name="btnConfigAdd">
<property name="toolTip">
<string>Create a new authentication configuration</string>
@ -146,6 +146,12 @@
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>

View File

@ -543,6 +543,7 @@ class TestQgsProcessing: public QObject
void parameterRasterLayer();
void parameterEnum();
void parameterString();
void parameterAuthConfig();
void parameterExpression();
void parameterField();
void parameterVectorLayer();
@ -4032,6 +4033,115 @@ void TestQgsProcessing::parameterString()
QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); // should be valid, falls back to valid default
}
void TestQgsProcessing::parameterAuthConfig()
{
QgsProcessingContext context;
// not optional!
std::unique_ptr< QgsProcessingParameterAuthConfig > def( new QgsProcessingParameterAuthConfig( "non_optional", QString(), QString(), false ) );
QVERIFY( def->checkValueIsAcceptable( 1 ) );
QVERIFY( def->checkValueIsAcceptable( "test" ) );
QVERIFY( !def->checkValueIsAcceptable( "" ) );
QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
// string
QVariantMap params;
params.insert( "non_optional", QString( "abcdef" ) );
QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QString( "abcdef" ) );
QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "'5'" ) );
QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc\ndef" ), context ), QStringLiteral( "'abc\\ndef'" ) );
QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\\\"complex\\\"'" ) );
QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) );
QString code = def->asScriptCode();
QCOMPARE( code, QStringLiteral( "##non_optional=authcfg" ) );
std::unique_ptr< QgsProcessingParameterAuthConfig > fromCode( dynamic_cast< QgsProcessingParameterAuthConfig * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
QVERIFY( fromCode.get() );
QCOMPARE( fromCode->name(), def->name() );
QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
QCOMPARE( fromCode->flags(), def->flags() );
QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
QVariantMap map = def->toVariantMap();
QgsProcessingParameterAuthConfig fromMap( "x" );
QVERIFY( fromMap.fromVariantMap( map ) );
QCOMPARE( fromMap.name(), def->name() );
QCOMPARE( fromMap.description(), def->description() );
QCOMPARE( fromMap.flags(), def->flags() );
QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
def.reset( dynamic_cast< QgsProcessingParameterAuthConfig *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
QVERIFY( dynamic_cast< QgsProcessingParameterAuthConfig *>( def.get() ) );
fromCode.reset( dynamic_cast< QgsProcessingParameterAuthConfig * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=authcfg None" ) ) ) );
QVERIFY( fromCode.get() );
QCOMPARE( fromCode->name(), def->name() );
QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
QCOMPARE( fromCode->flags(), def->flags() );
QVERIFY( !fromCode->defaultValue().isValid() );
fromCode.reset( dynamic_cast< QgsProcessingParameterAuthConfig * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=authcfg it's mario" ) ) ) );
QVERIFY( fromCode.get() );
QCOMPARE( fromCode->name(), def->name() );
QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
QCOMPARE( fromCode->flags(), def->flags() );
QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "it's mario" ) );
def->setDefaultValue( QStringLiteral( "it's mario" ) );
code = def->asScriptCode();
fromCode.reset( dynamic_cast< QgsProcessingParameterAuthConfig * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
QVERIFY( fromCode.get() );
QCOMPARE( fromCode->name(), def->name() );
QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
QCOMPARE( fromCode->flags(), def->flags() );
QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
fromCode.reset( dynamic_cast< QgsProcessingParameterAuthConfig * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=authcfg 'my val'" ) ) ) );
QVERIFY( fromCode.get() );
QCOMPARE( fromCode->name(), def->name() );
QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
QCOMPARE( fromCode->flags(), def->flags() );
QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "my val" ) );
fromCode.reset( dynamic_cast< QgsProcessingParameterAuthConfig * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=authcfg \"my val\"" ) ) ) );
QVERIFY( fromCode.get() );
QCOMPARE( fromCode->name(), def->name() );
QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
QCOMPARE( fromCode->flags(), def->flags() );
QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "my val" ) );
// optional
def.reset( new QgsProcessingParameterAuthConfig( "optional", QString(), QString( "default" ), true ) );
QVERIFY( def->checkValueIsAcceptable( 1 ) );
QVERIFY( def->checkValueIsAcceptable( "test" ) );
QVERIFY( def->checkValueIsAcceptable( "" ) );
QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
params.insert( "optional", QVariant() );
QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QString( "default" ) );
params.insert( "optional", QString() ); //empty string should not result in default value
QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QString() );
code = def->asScriptCode();
QCOMPARE( code, QStringLiteral( "##optional=optional authcfg default" ) );
fromCode.reset( dynamic_cast< QgsProcessingParameterAuthConfig * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
QVERIFY( fromCode.get() );
QCOMPARE( fromCode->name(), def->name() );
QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
QCOMPARE( fromCode->flags(), def->flags() );
QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
// not optional, valid default!
def.reset( new QgsProcessingParameterAuthConfig( "non_optional", QString(), QString( "def" ), false ) );
QVERIFY( def->checkValueIsAcceptable( 1 ) );
QVERIFY( def->checkValueIsAcceptable( "test" ) );
QVERIFY( !def->checkValueIsAcceptable( "" ) );
QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); // should be valid, falls back to valid default
}
void TestQgsProcessing::parameterExpression()
{
QgsProcessingContext context;

View File

@ -9,6 +9,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/tests/core #for render checker class
${CMAKE_SOURCE_DIR}/src/analysis/processing
${CMAKE_SOURCE_DIR}/src/gui
${CMAKE_SOURCE_DIR}/src/gui/auth
${CMAKE_SOURCE_DIR}/src/gui/editorwidgets
${CMAKE_SOURCE_DIR}/src/gui/editorwidgets/core
${CMAKE_SOURCE_DIR}/src/gui/layout
@ -32,6 +33,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_BINARY_DIR}/src/core
${CMAKE_BINARY_DIR}/src/gui
${CMAKE_BINARY_DIR}/src/ui
${CMAKE_BINARY_DIR}/src/ui/auth
${CMAKE_BINARY_DIR}/src/native
${CMAKE_CURRENT_BINARY_DIR}
)

View File

@ -41,6 +41,7 @@
#include "qgsdoublespinbox.h"
#include "qgsspinbox.h"
#include "qgsmapcanvas.h"
#include "qgsauthconfigselect.h"
#include "models/qgsprocessingmodelalgorithm.h"
class TestParamType : public QgsProcessingParameterDefinition
@ -146,6 +147,7 @@ class TestProcessingGui : public QObject
void testModelerWrapper();
void testBooleanWrapper();
void testStringWrapper();
void testAuthCfgWrapper();
void testCrsWrapper();
void testNumericWrapperDouble();
void testNumericWrapperInt();
@ -762,6 +764,89 @@ void TestProcessingGui::testStringWrapper()
delete l;
}
void TestProcessingGui::testAuthCfgWrapper()
{
QgsProcessingParameterAuthConfig param( QStringLiteral( "authcfg" ), QStringLiteral( "authcfg" ) );
// standard wrapper
QgsProcessingAuthConfigWidgetWrapper wrapper( &param );
QgsProcessingContext context;
QWidget *w = wrapper.createWrappedWidget( context );
QSignalSpy spy( &wrapper, &QgsProcessingAuthConfigWidgetWrapper::widgetValueHasChanged );
wrapper.setWidgetValue( QStringLiteral( "xxx" ), context );
QCOMPARE( spy.count(), 1 );
// hard to test these - we don't have a standard test authcfg to set to
// QCOMPARE( wrapper.widgetValue().toString(), QStringLiteral( "xxx" ) );
// QCOMPARE( static_cast< QgsAuthConfigSelect * >( wrapper.wrappedWidget() )->configId(), QStringLiteral( "xxx" ) );
wrapper.setWidgetValue( QString(), context );
QCOMPARE( spy.count(), 2 );
QVERIFY( wrapper.widgetValue().toString().isEmpty() );
QVERIFY( static_cast< QgsAuthConfigSelect * >( wrapper.wrappedWidget() )->configId().isEmpty() );
QLabel *l = wrapper.createWrappedLabel();
QVERIFY( l );
QCOMPARE( l->text(), QStringLiteral( "authcfg" ) );
QCOMPARE( l->toolTip(), param.toolTip() );
delete l;
// check signal
static_cast< QgsAuthConfigSelect * >( wrapper.wrappedWidget() )->setConfigId( QStringLiteral( "b" ) );
QCOMPARE( spy.count(), 3 );
delete w;
// batch wrapper
QgsProcessingAuthConfigWidgetWrapper wrapperB( &param, QgsProcessingGui::Batch );
w = wrapperB.createWrappedWidget( context );
QSignalSpy spy2( &wrapperB, &QgsProcessingAuthConfigWidgetWrapper::widgetValueHasChanged );
wrapperB.setWidgetValue( QStringLiteral( "a" ), context );
QCOMPARE( spy2.count(), 1 );
//QCOMPARE( wrapperB.widgetValue().toString(), QStringLiteral( "a" ) );
//QCOMPARE( static_cast< QgsAuthConfigSelect * >( wrapperB.wrappedWidget() )->configId(), QStringLiteral( "a" ) );
wrapperB.setWidgetValue( QString(), context );
QCOMPARE( spy2.count(), 2 );
QVERIFY( wrapperB.widgetValue().toString().isEmpty() );
QVERIFY( static_cast< QgsAuthConfigSelect * >( wrapperB.wrappedWidget() )->configId().isEmpty() );
// check signal
static_cast< QgsAuthConfigSelect * >( w )->setConfigId( QStringLiteral( "x" ) );
QCOMPARE( spy2.count(), 3 );
// should be no label in batch mode
QVERIFY( !wrapperB.createWrappedLabel() );
delete w;
// modeler wrapper
QgsProcessingAuthConfigWidgetWrapper wrapperM( &param, QgsProcessingGui::Modeler );
w = wrapperM.createWrappedWidget( context );
QSignalSpy spy3( &wrapperM, &QgsProcessingAuthConfigWidgetWrapper::widgetValueHasChanged );
wrapperM.setWidgetValue( QStringLiteral( "a" ), context );
//QCOMPARE( wrapperM.widgetValue().toString(), QStringLiteral( "a" ) );
QCOMPARE( spy3.count(), 1 );
//QCOMPARE( static_cast< QgsAuthConfigSelect * >( wrapperM.wrappedWidget() )->configId(), QStringLiteral( "a" ) );
wrapperM.setWidgetValue( QString(), context );
QVERIFY( wrapperM.widgetValue().toString().isEmpty() );
QCOMPARE( spy3.count(), 2 );
QVERIFY( static_cast< QgsAuthConfigSelect * >( wrapperM.wrappedWidget() )->configId().isEmpty() );
// check signal
static_cast< QgsAuthConfigSelect * >( w )->setConfigId( QStringLiteral( "x" ) );
QCOMPARE( spy3.count(), 3 );
// should be a label in modeler mode
l = wrapperM.createWrappedLabel();
QVERIFY( l );
QCOMPARE( l->text(), QStringLiteral( "authcfg" ) );
QCOMPARE( l->toolTip(), param.toolTip() );
delete w;
delete l;
}
void TestProcessingGui::testCrsWrapper()
{
QgsProcessingParameterCrs param( QStringLiteral( "crs" ), QStringLiteral( "crs" ) );