QGIS API Documentation 4.3.0-Master (bf28115e945)
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::SCHEMA_JSON] = QStringList { u"application/schema+json"_s };
36 map[QgsServerOgcApi::ContentType::GEOJSON] = QStringList { u"application/geo+json"_s, u"application/vnd.geo+json"_s, u"application/geojson"_s };
37 map[QgsServerOgcApi::ContentType::HTML] = QStringList { u"text/html"_s };
38 map[QgsServerOgcApi::ContentType::OPENAPI3] = QStringList { u"application/vnd.oai.openapi+json;version=3.0"_s };
39 map[QgsServerOgcApi::ContentType::XML] = QStringList { u"application/xml"_s };
40 map[QgsServerOgcApi::ContentType::FLATGEOBUF] = QStringList { u"application/flatgeobuf"_s, u"application/vnd.fgb"_s };
41 return map;
42}();
43
44QHash<QgsServerOgcApi::ContentType, QList<QgsServerOgcApi::ContentType>> QgsServerOgcApi::sContentTypeAliases = []() -> QHash<ContentType, QList<ContentType>> {
45 QHash<QgsServerOgcApi::ContentType, QList<QgsServerOgcApi::ContentType>> map;
47 return map;
48}();
49
50
51QgsServerOgcApi::QgsServerOgcApi( QgsServerInterface *serverIface, const QString &rootPath, const QString &name, const QString &description, const QString &version )
53 , mRootPath( rootPath )
54 , mName( name )
55 , mDescription( description )
56 , mVersion( version )
57{}
58
60{
61 //qDebug() << "API destroyed: " << name();
62}
63
65{
66 std::shared_ptr<QgsServerOgcApiHandler> hp( handler );
67 mHandlers.emplace_back( std::move( hp ) );
68}
69
70QUrl QgsServerOgcApi::sanitizeUrl( const QUrl &url )
71{
72 // Since QT 5.12 NormalizePathSegments does not collapse double slashes
73 QUrl u { url.adjusted( QUrl::StripTrailingSlash | QUrl::NormalizePathSegments ) };
74 if ( u.path().contains( "//"_L1 ) )
75 {
76 u.setPath( u.path().replace( "//"_L1, QChar( '/' ) ) );
77 }
78 // Make sure the path starts with '/'
79 if ( !u.path().startsWith( '/' ) )
80 {
81 u.setPath( u.path().prepend( '/' ) );
82 }
83 return u;
84}
85
87{
88 // Get url
89 const auto path { sanitizeUrl( context.handlerPath() ).path() };
90 // Find matching handler
91 auto hasMatch { false };
92 for ( const auto &handler : mHandlers )
93 {
94 QgsMessageLog::logMessage( u"Checking API path %1 for %2 "_s.arg( path, handler->path().pattern() ), u"Server"_s, Qgis::MessageLevel::Info );
95 if ( handler->path().match( path ).hasMatch() )
96 {
97 hasMatch = true;
98 // Execute handler
99 QgsMessageLog::logMessage( u"API %1: found handler %2"_s.arg( name(), QString::fromStdString( handler->operationId() ) ), u"Server"_s, Qgis::MessageLevel::Info );
100 // May throw QgsServerApiBadRequestException or JSON exceptions on serializing
101 try
102 {
103 handler->handleRequest( context );
104 }
105 catch ( json::exception &ex )
106 {
107 throw QgsServerApiInternalServerError( u"The API handler returned an error: %1"_s.arg( ex.what() ) );
108 }
109 break;
110 }
111 }
112 // Throw
113 if ( !hasMatch )
114 {
115 throw QgsServerApiBadRequestException( u"Requested URI does not match any registered API handler"_s );
116 }
117}
118
119const QMap<QgsServerOgcApi::ContentType, QStringList> QgsServerOgcApi::contentTypeMimes()
120{
121 return sContentTypeMime;
122}
123
124const QHash<QgsServerOgcApi::ContentType, QList<QgsServerOgcApi::ContentType>> QgsServerOgcApi::contentTypeAliases()
125{
126 return sContentTypeAliases;
127}
128
130{
131 switch ( profile )
132 {
133 case Profile::Rfc7946:
134 return u"json"_s;
135#if 0
136 // This not supported yet but I am leaving it here because
137 // I am very optimistic that it will be supported soon!
138 case Profile::JsonFg:
139 return u"jsonfg"_s;
140 case Profile::JsonFgPlus:
141 return u"jsonfg-plus"_s;
142#endif
143 case Profile::Unset:
144 return QString();
146 return u"rel-as-key"_s;
148 return u"rel-as-uri"_s;
150 return u"rel-as-link"_s;
151 }
152 Q_UNREACHABLE();
153 return QString();
154}
155
157{
158 switch ( profile )
159 {
160 case Profile::Rfc7946:
161 return u"http://www.opengis.net/def/profile/OGC/0/rfc7946"_s;
162#if 0
163 // This not supported yet but I am leaving it here because
164 // I am very optimistic that it will be supported soon!
165 case Profile::JsonFg:
166 return u"http://www.opengis.net/def/profile/OGC/0/jsonfg"_s;
167 case Profile::JsonFgPlus:
168 return u"http://www.opengis.net/def/profile/OGC/0/jsonfg-plus"_s;
169#endif
171 return u"http://www.opengis.net/def/profile/ogc/0/rel-as-key"_s;
173 return u"http://www.opengis.net/def/profile/ogc/0/rel-as-uri"_s;
175 return u"http://www.opengis.net/def/profile/ogc/0/rel-as-link"_s;
176 case Profile::Unset:
177 return QString();
178 }
179 Q_UNREACHABLE();
180 return QString();
181}
182
183std::string QgsServerOgcApi::relToString( const Rel &rel )
184{
185 if ( rel == Rel::schema )
186 {
187 return "http://www.opengis.net/def/rel/ogc/1.0/schema";
188 }
189 static const QMetaEnum metaEnum = QMetaEnum::fromType<QgsServerOgcApi::Rel>();
190 std::string val { metaEnum.valueToKey( rel ) };
191 std::replace( val.begin(), val.end(), '_', '-' );
192 return val;
193}
194
196{
197 static const QMetaEnum metaEnum = QMetaEnum::fromType<ContentType>();
198 QString result { metaEnum.valueToKey( ct ) };
199 return result.replace( '_', '-' );
200}
201
203{
204 static const QMetaEnum metaEnum = QMetaEnum::fromType<ContentType>();
205 return metaEnum.valueToKey( ct );
206}
207
209{
210 switch ( ct )
211 {
213 {
214 return u"json"_s;
215 }
217 {
218 return u"fgb"_s;
219 }
220 default:
221 return contentTypeToString( ct ).toLower();
222 }
223 // UNREACHABLE CODE
224}
225
227{
228 const QString exts = QString::fromStdString( extension );
229 const auto constMimeTypes( QgsServerOgcApi::contentTypeMimes() );
230 for ( auto it = constMimeTypes.constBegin(); it != constMimeTypes.constEnd(); ++it )
231 {
232 const auto constValues = it.value();
233 for ( const auto &value : constValues )
234 {
235 if ( value.contains( exts, Qt::CaseSensitivity::CaseInsensitive ) )
236 {
237 return it.key();
238 }
239 }
240 }
241 // Default to JSON, but log a warning!
242 QgsMessageLog::logMessage( u"Content type for extension %1 not found! Returning default (JSON)"_s.arg( exts ), u"Server"_s, Qgis::MessageLevel::Warning );
244}
245
247{
248 if ( !sContentTypeMime.contains( contentType ) )
249 {
250 return "";
251 }
252 return sContentTypeMime.value( contentType ).first().toStdString();
253}
254
255const std::vector<std::shared_ptr<QgsServerOgcApiHandler>> QgsServerOgcApi::handlers() const
256{
257 return mHandlers;
258}
@ 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.
@ SCHEMA_JSON
"application/schema+json"
@ 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.
@ profile
"http://www.opengis.net/spec/ogcapi-features-5/0.0/conf/feature-references"
@ schema
"http://www.opengis.net/def/rel/ogc/0.0/schema"
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.
@ RelAsUri
JSON responses that include URI for referenced resources http://www.opengis.net/def/profile/ogc/0/rel...
@ RelAsLink
JSON responses that include links for referenced resources http://www.opengis.net/def/profile/ogc/0/r...
@ RelAsKey
JSON responses that include key for referenced resources http://www.opengis.net/def/profile/ogc/0/rel...
static std::string relToString(const QgsServerOgcApi::Rel &rel)
Returns the string representation of rel attribute, to be used or "rel" link attribute in an OGC API ...
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.