19#include <nlohmann/json.hpp>
21#include "inja/inja.hpp"
36using namespace Qt::StringLiterals;
38using namespace nlohmann;
45 const auto constParameters {
parameters( context ) };
46 for (
const auto &p : constParameters )
49 result[p.name()] = p.value( context );
52 const auto match {
path().match( sanitizedPath ) };
53 if ( match.hasMatch() )
55 const auto constNamed {
path().namedCaptureGroups() };
57 for (
const auto &name : constNamed )
59 if ( !name.isEmpty() )
60 result[name] = QUrlQuery( match.captured( name ) ).toString();
91 for (
auto it = constContentTypes.constBegin(); it != constContentTypes.constEnd(); ++it )
93 const auto constValues = it.value();
94 for (
const auto &value : constValues )
96 if ( accept.contains( value, Qt::CaseSensitivity::CaseInsensitive ) )
111 switch ( contentType )
114 data[
"handler"] =
schema( context );
115 if ( !htmlMetadata.is_null() )
117 data[
"metadata"] = htmlMetadata;
144 if ( match.captured().count() > 0 )
146 url.setPath( urlBasePath + match.captured( 0 ) );
150 url.setPath( urlBasePath );
154 const auto suffixLength { QFileInfo( url.path() ).suffix().length() };
155 if ( suffixLength > 0 )
157 auto path { url.path() };
158 path.truncate(
path.length() - ( suffixLength + 1 ) );
163 url.setPath( url.path() + extraPath );
167 if ( !extension.isEmpty() )
170 QString
path { url.path() };
171 while (
path.endsWith(
'/' ) )
175 url.setPath(
path +
'.' + extension );
185 QDateTime time { QDateTime::currentDateTime() };
186 time.setTimeSpec( Qt::TimeSpec::UTC );
187 data[
"timeStamp"] = time.toString( Qt::DateFormat::ISODate ).toStdString();
210 {
"title", title !=
"" ? title :
linkTitle() },
218 json
links = json::array();
219 const QList<QgsServerOgcApi::ContentType> constCts {
contentTypes() };
220 for (
const auto &ct : constCts )
234 const QRegularExpressionMatch match {
path().match( context.
request()->
url().path() ) };
235 if ( !match.hasMatch() )
239 const QString collectionId { match.captured( u
"collectionId"_s ) };
254 path +=
"/ogc/templates"_L1;
267 if ( !QFile::exists(
path ) )
274 if ( !f.open( QFile::ReadOnly | QFile::Text ) )
283 QFileInfo pathInfo {
path };
284 Environment env { QString( pathInfo.dir().path() + QDir::separator() ).toStdString() };
287 env.add_callback(
"json_dump", 0, [data]( Arguments & ) {
return data.dump(); } );
290 env.add_callback(
"path_append", 1, [context]( Arguments &args ) {
292 QFileInfo fi { url.path() };
293 auto suffix { fi.suffix() };
294 auto fName { fi.filePath() };
295 if ( !suffix.isEmpty() )
297 fName.chop( suffix.length() + 1 );
300 while ( fName.endsWith(
'/' ) )
304 fName +=
'/' + QString::fromStdString( args.at( 0 )->get<std::string>() );
305 if ( !suffix.isEmpty() )
307 fName +=
'.' + suffix;
310 url.setPath( fi.filePath() );
311 return url.toString().toStdString();
315 env.add_callback(
"path_chomp", 1, []( Arguments &args ) {
316 QUrl url { QString::fromStdString( args.at( 0 )->get<std::string>() ) };
317 QFileInfo fi { url.path() };
318 auto suffix { fi.suffix() };
319 auto fName { fi.filePath() };
320 fName.chop( suffix.length() + 1 );
322 const thread_local QRegularExpression segmentRx( R
"raw(\/[^/]+$)raw" );
323 fName = fName.replace( segmentRx, QString() );
324 if ( !suffix.isEmpty() )
326 fName +=
'.' + suffix;
329 url.setPath( fi.filePath() );
330 return url.toString().toStdString();
335 env.add_callback(
"links_filter", 3, []( Arguments &args ) {
336 json
links = args.at( 0 )->get<json>();
337 if ( !
links.is_array() )
339 links = json::array();
341 std::string key { args.at( 1 )->get<std::string>() };
342 std::string value { args.at( 2 )->get<std::string>() };
343 json result = json::array();
344 for (
const auto &l :
links )
346 if ( l[key] == value )
348 result.push_back( l );
355 env.add_callback(
"content_type_name", 1, []( Arguments &args ) {
361 env.add_callback(
"nl2br", 1, []( Arguments &args ) {
362 QString text { QString::fromStdString( args.at( 0 )->get<std::string>() ) };
363 return text.replace(
'\n',
"<br>"_L1 ).toStdString();
369 env.add_callback(
"component_parameter", 1, [data]( Arguments &args ) {
370 json ret = json::array();
371 json ref = args.at( 0 )->get<json>();
372 if ( !ref.is_object() )
378 QString name = QString::fromStdString( ref[
"$ref"] );
379 name = name.split(
'/' ).last();
380 ret.push_back( data[
"components"][
"parameters"][name.toStdString()] );
382 catch ( std::exception & )
391 env.add_callback(
"static", 1, [context]( Arguments &args ) {
392 auto asset( args.at( 0 )->get<std::string>() );
395 if ( matchedPath ==
'/' )
399 return matchedPath.toStdString() +
"/static/" + asset;
404 env.add_callback(
"starts_with", 2, []( Arguments &args ) {
return string_view::starts_with( args.at( 0 )->get<std::string_view>(), args.at( 1 )->get<std::string_view>() ); } );
407 env.add_callback(
"if_nullptr_null_str", 1, []( Arguments &args ) {
408 json jsonValue = args.at( 0 )->get<json>();
410 switch ( jsonValue.type() )
413 case json::value_t::string:
414 out = jsonValue.get<std::string>();
417 case json::value_t::array:
418 case json::value_t::object:
419 if ( jsonValue.is_null() )
425 out = jsonValue.dump();
432 out = jsonValue.dump();
437 context.
response()->
write( env.render_file( pathInfo.fileName().toStdString(), data ) );
439 catch ( std::exception &e )
450 bool found {
false };
452 const QString extension { QFileInfo( request->
url().path() ).suffix().toUpper() };
453 if ( !extension.isEmpty() )
455 static QMetaEnum metaEnum { QMetaEnum::fromType<QgsServerOgcApi::ContentType>() };
457 const int ct { metaEnum.keyToValue( extension.toLocal8Bit().constData(), &ok ) };
469 const QString accept { request->
header( u
"Accept"_s ) };
470 if ( !found && !accept.isEmpty() )
473 if ( !ctFromAccept.isEmpty() )
476 auto it = constContentTypes.constBegin();
477 while ( !found && it != constContentTypes.constEnd() )
479 int idx = it.value().indexOf( ctFromAccept );
497 bool found {
false };
500 const QList<QgsServerOgcApi::ContentType> constCt {
contentTypes() };
501 for (
const auto &ct : constCt )
523 QString
path { url.path() };
524 const QFileInfo fi {
path };
525 const QString suffix { fi.suffix() };
526 if ( !suffix.isEmpty() )
528 path.chop( suffix.length() + 1 );
530 while (
path.endsWith(
'/' ) )
534 const thread_local QRegularExpression re( R
"raw(\/[^/]+$)raw" );
535 for (
int i = 0; i < levels; i++ )
537 path =
path.replace( re, QString() );
540 QUrlQuery query( result );
541 QList<QPair<QString, QString>> qi;
542 const auto constItems { query.queryItems() };
543 for (
const auto &i : constItems )
545 if ( i.first.compare( u
"MAP"_s, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
551 if ( !
path.endsWith(
'/' ) )
555 QUrlQuery resultQuery;
556 resultQuery.setQueryItems( qi );
557 result.setQuery( resultQuery );
558 result.setPath(
path );
559 return result.toString();
565 if ( mapLayers.count() != 1 )
567 throw QgsServerApiNotFoundError( u
"Collection with given id (%1) was not found or multiple matches were found"_s.arg( collectionId ) );
569 return mapLayers.first();
575 = { {
"description",
"An error occurred." }, {
"content", { {
"application/json", { {
"schema", { {
"$ref",
"#/components/schemas/exception" } } } } }, {
"text/html", { {
"schema", { {
"type",
"string" } } } } } } } };
586 mContentTypes.clear();
@ Warning
Warning message.
@ Critical
Critical/error message.
@ Info
Information message.
static json jsonFromVariant(const QVariant &v)
Converts a QVariant v to a json object.
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).
QList< QgsMapLayer * > mapLayersByShortName(const QString &shortName) const
Retrieves a list of matching registered layers by layer shortName.
Bad request error API exception.
Encapsulates the resources for a particular client request.
const QgsProject * project() const
Returns the (possibly NULL) project.
QgsServerResponse * response() const
Returns the server response object.
QString handlerPath() const
Returns the handler component of the URL path, i.e.
const QgsServerRequest * request() const
Returns the server request object.
QgsServerInterface * serverInterface() const
Returns the server interface.
QString apiRootPath() const
Returns the API root path.
const QString matchedPath() const
Returns the initial part of the incoming request URL path that matches the API root path.
Internal server error API exception.
Not found error API exception.
Raised when the client requested a method that is not yet implemented.
virtual QgsServerSettings * serverSettings()=0
Returns the server settings.
void jsonDump(json &data, const QgsServerApiContext &context, const QString &contentType=u"application/json"_s) const
Writes data to the context response stream as JSON (indented if debug is active), an optional content...
std::string href(const QgsServerApiContext &context, const QString &extraPath=QString(), const QString &extension=QString()) const
Returns an URL to self, to be used for links to the current resources and as a base for constructing ...
virtual const QString templatePath(const QgsServerApiContext &context) const
Returns the HTML template path for the handler in the given context.
virtual const QString staticPath(const QgsServerApiContext &context) const
Returns the absolute path to the base directory where static resources for this handler are stored in...
virtual QVariantMap values(const QgsServerApiContext &context) const
Analyzes the incoming request context and returns the validated parameter map, throws QgsServerApiBad...
json jsonTags() const
Returns tags as JSON.
void htmlDump(const json &data, const QgsServerApiContext &context) const
Writes data as HTML to the response stream in context using a template.
virtual QgsServerOgcApi::ContentType defaultContentType() const
Returns the default response content type in case the client did not specifically ask for any particu...
QgsServerOgcApi::ContentType contentTypeFromRequest(const QgsServerRequest *request) const
Returns the content type from the request.
virtual QgsServerOgcApi::Rel linkType() const =0
Main role for the resource link.
virtual ~QgsServerOgcApiHandler()
virtual json schema(const QgsServerApiContext &context) const
Returns handler information from the context for the OPENAPI description (id, description and other m...
virtual QStringList tags() const
Tags.
void setContentTypes(const QList< QgsServerOgcApi::ContentType > &contentTypes)
Set the content types to contentTypes.
void write(json &data, const QgsServerApiContext &context, const json &htmlMetadata=nullptr) const
Writes data to the context response stream, content-type is calculated from the context request,...
QList< QgsServerOgcApi::ContentType > contentTypes() const
Returns the list of content types this handler can serve, default to JSON and HTML.
virtual std::string linkTitle() const =0
Title for the handler link.
virtual QList< QgsServerQueryStringParameter > parameters(const QgsServerApiContext &context) const
Returns a list of query string parameters.
virtual std::string operationId() const =0
Returns the operation id for template file names and other internal references.
static json defaultResponse()
Returns the defaultResponse as JSON.
QgsVectorLayer * layerFromContext(const QgsServerApiContext &context) const
Returns a vector layer instance from the "collectionId" parameter of the path in the given context,...
QString contentTypeForAccept(const QString &accept) const
Looks for the first ContentType match in the accept header and returns its mime type,...
virtual void handleRequest(const QgsServerApiContext &context) const
Handles the request within its context.
json link(const QgsServerApiContext &context, const QgsServerOgcApi::Rel &linkType=QgsServerOgcApi::Rel::self, const QgsServerOgcApi::ContentType contentType=QgsServerOgcApi::ContentType::JSON, const std::string &title="") const
Builds and returns a link to the resource.
json links(const QgsServerApiContext &context) const
Returns all the links for the given request context.
static QString parentLink(const QUrl &url, int levels=1)
Returns a link to the parent page up to levels in the HTML hierarchy from the given url,...
virtual QRegularExpression path() const =0
URL pattern for this handler, named capture group are automatically extracted and returned by values(...
void setContentTypesInt(const QList< int > &contentTypes)
Set the content types to contentTypes.
static QgsVectorLayer * layerFromCollectionId(const QgsServerApiContext &context, const QString &collectionId)
Returns a vector layer from the collectionId in the given context.
static QUrl sanitizeUrl(const QUrl &url)
Returns a sanitized url with extra slashes removed and the path URL component that always starts with...
static QString contentTypeToExtension(const QgsServerOgcApi::ContentType &ct)
Returns the file extension for a ct (Content-Type).
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"
static QString contentTypeToString(const QgsServerOgcApi::ContentType &ct)
Returns the string representation of a ct (Content-Type) attribute.
@ alternate
Refers to a substitute for this context.
@ self
Conveys an identifier for the link’s context.
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.
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.
Defines requests passed to QgsService classes.
virtual QString header(const QString &name) const
Returns the header value.
QUrl url() const
Returns the request URL as seen by QGIS server.
virtual void write(const QString &data)
Write string This is a convenient method that will write directly to the underlying I/O device.
virtual void setHeader(const QString &key, const QString &value)=0
Set Header entry Add Header entry to the response Note that it is usually an error to set Header afte...
virtual void setStatusCode(int code)=0
Set the http status code.
QString apiResourcesDirectory() const
Returns the server-wide base directory where HTML templates and static assets (e.g.
Represents a vector layer which manages a vector based dataset.