Skip to content
Permalink
Browse files

[FEATURE] Arcgis Feature Service connections which have their

corresponding Portal endpoint URLS set can be explored by content
groups in the browser panel.

If a connection has the Portal endpoints set, then expanding out
the connection in the browser will show a "Groups" and "Services"
folder, instead of the full list of services usually shown. Expanding
out the groups folder will show a list of all content groups that
the user is a member of, each of which can be expanded to show
the service items belonging to that group.
  • Loading branch information
nyalldawson committed Dec 21, 2020
1 parent 95b596a commit 60001d863375df021a3cec879f39ce96c270e711
Showing with 263 additions and 12 deletions.
  1. +183 −11 src/providers/arcgisrest/qgsafsdataitems.cpp
  2. +80 −1 src/providers/arcgisrest/qgsafsdataitems.h
@@ -18,6 +18,7 @@
#include "qgsafsprovider.h"
#include "qgsarcgisrestutils.h"
#include "qgsarcgisrestquery.h"
#include "qgsarcgisportalutils.h"

#ifdef HAVE_GUI
#include "qgsafssourceselect.h"
@@ -27,6 +28,7 @@
#include <QCoreApplication>
#include <QSettings>
#include <QUrl>
#include "qgssettings.h"


QgsAfsRootItem::QgsAfsRootItem( QgsDataItem *parent, const QString &name, const QString &path )
@@ -130,6 +132,11 @@ QgsAfsConnectionItem::QgsAfsConnectionItem( QgsDataItem *parent, const QString &
{
mIconName = QStringLiteral( "mIconConnect.svg" );
mCapabilities |= Collapse;

QgsSettings settings;
const QString key = QStringLiteral( "qgis/connections-arcgisfeatureserver/" ) + mConnName;
mPortalContentEndpoint = settings.value( key + "/content_endpoint" ).toString();
mPortalCommunityEndpoint = settings.value( key + "/community_endpoint" ).toString();
}

QVector<QgsDataItem *> QgsAfsConnectionItem::createChildren()
@@ -143,9 +150,73 @@ QVector<QgsDataItem *> QgsAfsConnectionItem::createChildren()
headers[ QStringLiteral( "Referer" )] = referer;

QVector<QgsDataItem *> items;
QString errorTitle, errorMessage;
const QVariantMap serviceData = QgsArcGisRestQueryUtils::getServiceInfo( url, authcfg, errorTitle, errorMessage, headers );
if ( serviceData.isEmpty() )
if ( !mPortalCommunityEndpoint.isEmpty() && !mPortalContentEndpoint.isEmpty() )
{
items << new QgsAfsPortalGroupsItem( this, QStringLiteral( "groups" ), authcfg, headers, mPortalCommunityEndpoint, mPortalContentEndpoint );
items << new QgsAfsServicesItem( this, url, QStringLiteral( "services" ), authcfg, headers );
}
else
{
QString errorTitle, errorMessage;
const QVariantMap serviceData = QgsArcGisRestQueryUtils::getServiceInfo( url, authcfg, errorTitle, errorMessage, headers );
if ( serviceData.isEmpty() )
{
if ( !errorMessage.isEmpty() )
{
std::unique_ptr< QgsErrorItem > error = qgis::make_unique< QgsErrorItem >( this, tr( "Connection failed: %1" ).arg( errorTitle ), mPath + "/error" );
error->setToolTip( errorMessage );
items.append( error.release() );
QgsDebugMsg( "Connection failed - " + errorMessage );
}
return items;
}

addFolderItems( items, serviceData, url, authcfg, headers, this );
addServiceItems( items, serviceData, url, authcfg, headers, this );
addLayerItems( items, serviceData, url, authcfg, headers, this );
}

return items;
}

bool QgsAfsConnectionItem::equal( const QgsDataItem *other )
{
const QgsAfsConnectionItem *o = qobject_cast<const QgsAfsConnectionItem *>( other );
return ( type() == other->type() && o && mPath == o->mPath && mName == o->mName );
}

QString QgsAfsConnectionItem::url() const
{
const QgsOwsConnection connection( QStringLiteral( "ARCGISFEATURESERVER" ), mConnName );
return connection.uri().param( QStringLiteral( "url" ) );
}


//
// QgsAfsGroupsItem
//

QgsAfsPortalGroupsItem::QgsAfsPortalGroupsItem( QgsDataItem *parent, const QString &path, const QString &authcfg, const QgsStringMap &headers, const QString &communityEndpoint, const QString &contentEndpoint )
: QgsDataCollectionItem( parent, tr( "Groups" ), path, QStringLiteral( "AFS" ) )
, mAuthCfg( authcfg )
, mHeaders( headers )
, mPortalCommunityEndpoint( communityEndpoint )
, mPortalContentEndpoint( contentEndpoint )
{
mIconName = QStringLiteral( "mIconDbSchema.svg" );
mCapabilities |= Collapse;
setToolTip( path );
}


QVector<QgsDataItem *> QgsAfsPortalGroupsItem::createChildren()
{
QVector<QgsDataItem *> items;

QString errorTitle;
QString errorMessage;
const QVariantList groups = QgsArcGisPortalUtils::retrieveUserGroups( mPortalCommunityEndpoint, QString(), mAuthCfg, errorTitle, errorMessage, mHeaders );
if ( groups.isEmpty() )
{
if ( !errorMessage.isEmpty() )
{
@@ -157,26 +228,127 @@ QVector<QgsDataItem *> QgsAfsConnectionItem::createChildren()
return items;
}

addFolderItems( items, serviceData, url, authcfg, headers, this );
addServiceItems( items, serviceData, url, authcfg, headers, this );
addLayerItems( items, serviceData, url, authcfg, headers, this );
for ( const QVariant &group : groups )
{
const QVariantMap groupData = group.toMap();
items << new QgsAfsPortalGroupItem( this, groupData.value( QStringLiteral( "id" ) ).toString(),
groupData.value( QStringLiteral( "title" ) ).toString(),
mAuthCfg, mHeaders, mPortalCommunityEndpoint, mPortalContentEndpoint );
items.last()->setToolTip( groupData.value( QStringLiteral( "snippet" ) ).toString() );
}

return items;
}

bool QgsAfsConnectionItem::equal( const QgsDataItem *other )
bool QgsAfsPortalGroupsItem::equal( const QgsDataItem *other )
{
const QgsAfsConnectionItem *o = qobject_cast<const QgsAfsConnectionItem *>( other );
const QgsAfsPortalGroupsItem *o = qobject_cast<const QgsAfsPortalGroupsItem *>( other );
return ( type() == other->type() && o && mPath == o->mPath && mName == o->mName );
}

QString QgsAfsConnectionItem::url() const

//
// QgsAfsGroupItem
//
QgsAfsPortalGroupItem::QgsAfsPortalGroupItem( QgsDataItem *parent, const QString &groupId, const QString &name, const QString &authcfg, const QgsStringMap &headers, const QString &communityEndpoint, const QString &contentEndpoint )
: QgsDataCollectionItem( parent, name, groupId, QStringLiteral( "AFS" ) )
, mId( groupId )
, mAuthCfg( authcfg )
, mHeaders( headers )
, mPortalCommunityEndpoint( communityEndpoint )
, mPortalContentEndpoint( contentEndpoint )
{
const QgsOwsConnection connection( QStringLiteral( "ARCGISFEATURESERVER" ), mConnName );
return connection.uri().param( QStringLiteral( "url" ) );
mIconName = QStringLiteral( "mIconDbSchema.svg" );
mCapabilities |= Collapse;
setToolTip( name );
}

QVector<QgsDataItem *> QgsAfsPortalGroupItem::createChildren()
{
QVector<QgsDataItem *> items;

QString errorTitle;
QString errorMessage;
const QVariantList groupItems = QgsArcGisPortalUtils::retrieveGroupItemsOfType( mPortalContentEndpoint, mId, mAuthCfg, QList<int >() << QgsArcGisPortalUtils::FeatureService,
errorTitle, errorMessage, mHeaders );
if ( groupItems.isEmpty() )
{
if ( !errorMessage.isEmpty() )
{
std::unique_ptr< QgsErrorItem > error = qgis::make_unique< QgsErrorItem >( this, tr( "Connection failed: %1" ).arg( errorTitle ), mPath + "/error" );
error->setToolTip( errorMessage );
items.append( error.release() );
QgsDebugMsg( "Connection failed - " + errorMessage );
}
return items;
}

for ( const QVariant &item : groupItems )
{
const QVariantMap itemData = item.toMap();
items << new QgsAfsServiceItem( this, itemData.value( QStringLiteral( "title" ) ).toString(),
itemData.value( QStringLiteral( "url" ) ).toString(),
itemData.value( QStringLiteral( "url" ) ).toString(), mAuthCfg, mHeaders );
}

return items;
}

bool QgsAfsPortalGroupItem::equal( const QgsDataItem *other )
{
const QgsAfsPortalGroupItem *o = qobject_cast<const QgsAfsPortalGroupItem *>( other );
return ( type() == other->type() && o && mId == o->mId && mName == o->mName );
}


//
// QgsAfsServicesItem
//

QgsAfsServicesItem::QgsAfsServicesItem( QgsDataItem *parent, const QString &url, const QString &path, const QString &authcfg, const QgsStringMap &headers )
: QgsDataCollectionItem( parent, tr( "Services" ), path, QStringLiteral( "AFS" ) )
, mUrl( url )
, mAuthCfg( authcfg )
, mHeaders( headers )
{
mIconName = QStringLiteral( "mIconDbSchema.svg" );
mCapabilities |= Collapse;
}

QVector<QgsDataItem *> QgsAfsServicesItem::createChildren()
{
QVector<QgsDataItem *> items;
QString errorTitle, errorMessage;
const QVariantMap serviceData = QgsArcGisRestQueryUtils::getServiceInfo( mUrl, mAuthCfg, errorTitle, errorMessage, mHeaders );
if ( serviceData.isEmpty() )
{
if ( !errorMessage.isEmpty() )
{
std::unique_ptr< QgsErrorItem > error = qgis::make_unique< QgsErrorItem >( this, tr( "Connection failed: %1" ).arg( errorTitle ), mPath + "/error" );
error->setToolTip( errorMessage );
items.append( error.release() );
QgsDebugMsg( "Connection failed - " + errorMessage );
}
return items;
}

addFolderItems( items, serviceData, mUrl, mAuthCfg, mHeaders, this );
addServiceItems( items, serviceData, mUrl, mAuthCfg, mHeaders, this );
addLayerItems( items, serviceData, mUrl, mAuthCfg, mHeaders, this );
return items;
}

bool QgsAfsServicesItem::equal( const QgsDataItem *other )
{
const QgsAfsServicesItem *o = qobject_cast<const QgsAfsServicesItem *>( other );
return ( type() == other->type() && o && mPath == o->mPath && mName == o->mName );
}



//
// QgsAfsFolderItem
//
QgsAfsFolderItem::QgsAfsFolderItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &baseUrl, const QString &authcfg, const QgsStringMap &headers )
: QgsDataCollectionItem( parent, name, path, QStringLiteral( "AFS" ) )
, mBaseUrl( baseUrl )
@@ -40,7 +40,13 @@ class QgsAfsRootItem : public QgsConnectionsRootItem
#endif
};


/**
* Root item corresponding to a connection.
*
* Depending on whether the connection has the Portal endpoints set, child items will either be
* QgsAfsServiceItems directly, or a QgsAfsPortalGroupsItem and QgsAfsServicesItem allowing the user
* to explore content by services or by Portal content groups.
*/
class QgsAfsConnectionItem : public QgsDataCollectionItem
{
Q_OBJECT
@@ -52,8 +58,81 @@ class QgsAfsConnectionItem : public QgsDataCollectionItem

private:
QString mConnName;
QString mPortalCommunityEndpoint;
QString mPortalContentEndpoint;
};

/**
* A container for all ArcGIS Portal user groups present on a server (which the user belongs to).
*
* Child items are QgsAfsPortalGroupItem items for each group.
*
* \note This is only used for AFS connections which have the Portal endpoints set.
*/
class QgsAfsPortalGroupsItem : public QgsDataCollectionItem
{
Q_OBJECT
public:
QgsAfsPortalGroupsItem( QgsDataItem *parent, const QString &path, const QString &authcfg, const QgsStringMap &headers,
const QString &communityEndpoint, const QString &contentEndpoint );
QVector<QgsDataItem *> createChildren() override;
bool equal( const QgsDataItem *other ) override;

private:
QString mAuthCfg;
QgsStringMap mHeaders;
QString mPortalCommunityEndpoint;
QString mPortalContentEndpoint;
};

/**
* Represents a single ArcGIS Portal user group. Child items are QgsAfsServiceItem representing each feature service
* present within the group.
*
* \note This is only used for AFS connections which have the Portal endpoints set.
*/
class QgsAfsPortalGroupItem : public QgsDataCollectionItem
{
Q_OBJECT
public:
QgsAfsPortalGroupItem( QgsDataItem *parent, const QString &groupId, const QString &name, const QString &authcfg, const QgsStringMap &headers,
const QString &communityEndpoint, const QString &contentEndpoint );
QVector<QgsDataItem *> createChildren() override;
bool equal( const QgsDataItem *other ) override;

private:
QString mId;
QString mAuthCfg;
QgsStringMap mHeaders;
QString mPortalCommunityEndpoint;
QString mPortalContentEndpoint;
};


/**
* A container for all ArcGIS Portal services present on a server.
*
* Child items are QgsAfsServiceItem items.
*
* \note This is only used for AFS connections which have the Portal endpoints set.
*/
class QgsAfsServicesItem : public QgsDataCollectionItem
{
Q_OBJECT
public:
QgsAfsServicesItem( QgsDataItem *parent, const QString &url, const QString &path, const QString &authcfg, const QgsStringMap &headers );
QVector<QgsDataItem *> createChildren() override;
bool equal( const QgsDataItem *other ) override;

private:
QString mUrl;
QString mAuthCfg;
QgsStringMap mHeaders;
QString mPortalCommunityEndpoint;
QString mPortalContentEndpoint;
};


class QgsAfsFolderItem : public QgsDataCollectionItem
{
Q_OBJECT

0 comments on commit 60001d8

Please sign in to comment.
You can’t perform that action at this time.