QGIS API Documentation 4.1.0-Master (31622b25bb0)
Loading...
Searching...
No Matches
qgsserverogcapi.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsserverogcapi.cpp - QgsServerOgcApi
3
4 ---------------------
5 begin : 10.7.2019
6 copyright : (C) 2019 by Alessandro Pasotti
7 email : elpaso at itopen dot it
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17#include "qgsserverogcapi.h"
18
19#include "qgsapplication.h"
20#include "qgsmessagelog.h"
22
23#include <QDebug>
24#include <QDir>
25#include <QString>
26#include <QtGlobal>
27
28#include "moc_qgsserverogcapi.cpp"
29
30using namespace Qt::StringLiterals;
31
32QMap<QgsServerOgcApi::ContentType, QStringList> QgsServerOgcApi::sContentTypeMime = []() -> QMap<QgsServerOgcApi::ContentType, QStringList> {
33 QMap<QgsServerOgcApi::ContentType, QStringList> map;
34 map[QgsServerOgcApi::ContentType::JSON] = QStringList { u"application/json"_s };
35 map[QgsServerOgcApi::ContentType::GEOJSON] = QStringList { u"application/geo+json"_s, u"application/vnd.geo+json"_s, u"application/geojson"_s };
36 map[QgsServerOgcApi::ContentType::HTML] = QStringList { u"text/html"_s };
37 map[QgsServerOgcApi::ContentType::OPENAPI3] = QStringList { u"application/vnd.oai.openapi+json;version=3.0"_s };
38 map[QgsServerOgcApi::ContentType::XML] = QStringList { u"application/xml"_s };
39 map[QgsServerOgcApi::ContentType::FLATGEOBUF] = QStringList { u"application/flatgeobuf"_s, u"application/vnd.fgb"_s };
40 return map;
41}();
42
43QHash<QgsServerOgcApi::ContentType, QList<QgsServerOgcApi::ContentType>> QgsServerOgcApi::sContentTypeAliases = []() -> QHash<ContentType, QList<ContentType>> {
44 QHash<QgsServerOgcApi::ContentType, QList<QgsServerOgcApi::ContentType>> map;
46 return map;
47}();
48
49
50QgsServerOgcApi::QgsServerOgcApi( QgsServerInterface *serverIface, const QString &rootPath, const QString &name, const QString &description, const QString &version )
52 , mRootPath( rootPath )
53 , mName( name )
54 , mDescription( description )
55 , mVersion( version )
56{}
57
59{
60 //qDebug() << "API destroyed: " << name();
61}
62
64{
65 std::shared_ptr<QgsServerOgcApiHandler> hp( handler );
66 mHandlers.emplace_back( std::move( hp ) );
67}
68
69QUrl QgsServerOgcApi::sanitizeUrl( const QUrl &url )
70{
71 // Since QT 5.12 NormalizePathSegments does not collapse double slashes
72 QUrl u { url.adjusted( QUrl::StripTrailingSlash | QUrl::NormalizePathSegments ) };
73 if ( u.path().contains( "//"_L1 ) )
74 {
75 u.setPath( u.path().replace( "//"_L1, QChar( '/' ) ) );
76 }
77 // Make sure the path starts with '/'
78 if ( !u.path().startsWith( '/' ) )
79 {
80 u.setPath( u.path().prepend( '/' ) );
81 }
82 return u;
83}
84
86{
87 // Get url
88 const auto path { sanitizeUrl( context.handlerPath() ).path() };
89 // Find matching handler
90 auto hasMatch { false };
91 for ( const auto &handler : mHandlers )
92 {
93 QgsMessageLog::logMessage( u"Checking API path %1 for %2 "_s.arg( path, handler->path().pattern() ), u"Server"_s, Qgis::MessageLevel::Info );
94 if ( handler->path().match( path ).hasMatch() )
95 {
96 hasMatch = true;
97 // Execute handler
98 QgsMessageLog::logMessage( u"API %1: found handler %2"_s.arg( name(), QString::fromStdString( handler->operationId() ) ), u"Server"_s, Qgis::MessageLevel::Info );
99 // May throw QgsServerApiBadRequestException or JSON exceptions on serializing
100 try
101 {
102 handler->handleRequest( context );
103 }
104 catch ( json::exception &ex )
105 {
106 throw QgsServerApiInternalServerError( u"The API handler returned an error: %1"_s.arg( ex.what() ) );
107 }
108 break;
109 }
110 }
111 // Throw
112 if ( !hasMatch )
113 {
114 throw QgsServerApiBadRequestException( u"Requested URI does not match any registered API handler"_s );
115 }
116}
117
118const QMap<QgsServerOgcApi::ContentType, QStringList> QgsServerOgcApi::contentTypeMimes()
119{
120 return sContentTypeMime;
121}
122
123const QHash<QgsServerOgcApi::ContentType, QList<QgsServerOgcApi::ContentType>> QgsServerOgcApi::contentTypeAliases()
124{
125 return sContentTypeAliases;
126}
127
129{
130 switch ( profile )
131 {
132 case Profile::RFC7946:
133 return u"json"_s;
134#if 0
135 // This not supported yet but I am leaving it here because
136 // I am very optimistic that it will be supported soon!
137 case Profile::JSONFG:
138 return u"jsonfg"_s;
139 case Profile::JSONFG_PLUS:
140 return u"jsonfg-plus"_s;
141#endif
142 case Profile::NONE:
143 return QString();
144 }
145 Q_UNREACHABLE();
146 return QString();
147}
148
149QString QgsServerOgcApi::profileToUri( const Profile &profile )
150{
151 switch ( profile )
152 {
153 case Profile::RFC7946:
154 return u"http://www.opengis.net/def/profile/OGC/0/rfc7946"_s;
155#if 0
156 // This not supported yet but I am leaving it here because
157 // I am very optimistic that it will be supported soon!
158 case Profile::JSONFG:
159 return u"http://www.opengis.net/def/profile/OGC/0/jsonfg"_s;
160 case Profile::JSONFG_PLUS:
161 return u"http://www.opengis.net/def/profile/OGC/0/jsonfg-plus"_s;
162#endif
163 case Profile::NONE:
164 return QString();
165 }
166 Q_UNREACHABLE();
167 return QString();
168}
169
170std::string QgsServerOgcApi::relToString( const Rel &rel )
171{
172 static const QMetaEnum metaEnum = QMetaEnum::fromType<QgsServerOgcApi::Rel>();
173 std::string val { metaEnum.valueToKey( rel ) };
174 std::replace( val.begin(), val.end(), '_', '-' );
175 return val;
176}
177
179{
180 static const QMetaEnum metaEnum = QMetaEnum::fromType<ContentType>();
181 QString result { metaEnum.valueToKey( ct ) };
182 return result.replace( '_', '-' );
183}
184
186{
187 static const QMetaEnum metaEnum = QMetaEnum::fromType<ContentType>();
188 return metaEnum.valueToKey( ct );
189}
190
192{
193 const QString extension { contentTypeToString( ct ).toLower() };
194 // Special case for flatgeobuf, which is a bit too long for
195 // an extension and has a widely used alternative (fgb)
196 if ( extension.compare( u"flatgeobuf"_s, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
197 {
198 return u"fgb"_s;
199 }
200 return extension;
201}
202
204{
205 const QString exts = QString::fromStdString( extension );
206 const auto constMimeTypes( QgsServerOgcApi::contentTypeMimes() );
207 for ( auto it = constMimeTypes.constBegin(); it != constMimeTypes.constEnd(); ++it )
208 {
209 const auto constValues = it.value();
210 for ( const auto &value : constValues )
211 {
212 if ( value.contains( exts, Qt::CaseSensitivity::CaseInsensitive ) )
213 {
214 return it.key();
215 }
216 }
217 }
218 // Default to JSON, but log a warning!
219 QgsMessageLog::logMessage( u"Content type for extension %1 not found! Returning default (JSON)"_s.arg( exts ), u"Server"_s, Qgis::MessageLevel::Warning );
221}
222
224{
225 if ( !sContentTypeMime.contains( contentType ) )
226 {
227 return "";
228 }
229 return sContentTypeMime.value( contentType ).first().toStdString();
230}
231
232const std::vector<std::shared_ptr<QgsServerOgcApiHandler>> QgsServerOgcApi::handlers() const
233{
234 return mHandlers;
235}
@ Warning
Warning message.
Definition qgis.h:162
@ Info
Information message.
Definition qgis.h:161
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
Bad request error API exception.
Encapsulates the resources for a particular client request.
QString handlerPath() const
Returns the handler component of the URL path, i.e.
Internal server error API exception.
QgsServerApi(QgsServerInterface *serverIface)
Creates a QgsServerApi object.
QgsServerInterface * serverIface() const
Returns the server interface.
Defines interfaces exposed by QGIS Server and made available to plugins.
An abstract class which represents an OGC API handler to be registered in QgsServerOgcApi class.
static QUrl sanitizeUrl(const QUrl &url)
Returns a sanitized url with extra slashes removed and the path URL component that always starts with...
void executeRequest(const QgsServerApiContext &context) const override
Executes a request by passing the given context to the API handlers.
const QString rootPath() const override
Returns the root path for the API.
void registerHandler(Args... args)
Registers an OGC API handler passing Args to the constructor.
QgsServerOgcApi(QgsServerInterface *serverIface, const QString &rootPath, const QString &name, const QString &description=QString(), const QString &version=QString())
QgsServerOgcApi constructor.
static QString contentTypeToExtension(const QgsServerOgcApi::ContentType &ct)
Returns the file extension for a ct (Content-Type).
const QString description() const override
Returns the API description.
const std::vector< std::shared_ptr< QgsServerOgcApiHandler > > handlers() const
Returns registered handlers.
static const QMap< QgsServerOgcApi::ContentType, QStringList > contentTypeMimes()
Returns a map of contentType => list of mime types.
ContentType
Media types used for content negotiation, insert more specific first.
@ OPENAPI3
"application/openapi+json;version=3.0"
@ FLATGEOBUF
"application/flatgeobuf"
static QString contentTypeToString(const QgsServerOgcApi::ContentType &ct)
Returns the string representation of a ct (Content-Type) attribute.
static QString profileToString(const QgsServerOgcApi::Profile &profile)
Returns a string representation of the profile.
static QString profileToUri(const QgsServerOgcApi::Profile &profile)
Returns a URI string representation of the profile.
Rel
Rel link types.
static std::string contentTypeToStdString(const QgsServerOgcApi::ContentType &ct)
Returns the string representation of a ct (Content-Type) attribute.
static std::string mimeType(const QgsServerOgcApi::ContentType &contentType)
Returns the mime-type for the contentType or an empty string if not found.
const QString version() const override
Returns the version of the service.
~QgsServerOgcApi() override
const QString name() const override
Returns the API name.
Profile
JSON profile.
@ RFC7946
GeoJSON profile according to RFC7946.
static std::string relToString(const QgsServerOgcApi::Rel &rel)
Returns the string representation of rel attribute.
static const QHash< QgsServerOgcApi::ContentType, QList< QgsServerOgcApi::ContentType > > contentTypeAliases()
Returns contentType specializations (e.g.
static QgsServerOgcApi::ContentType contentTypeFromExtension(const std::string &extension)
Returns the Content-Type value corresponding to extension.