32#include "nlohmann/json.hpp"
33#include "inja/inja.hpp"
35using namespace nlohmann;
42 const auto constParameters {
parameters( context ) };
43 for (
const auto &p : constParameters )
46 result[p.name()] = p.value( context );
49 const auto match {
path().match( sanitizedPath ) };
50 if ( match.hasMatch() )
52 const auto constNamed {
path().namedCaptureGroups() };
54 for (
const auto &name : constNamed )
56 if ( !name.isEmpty() )
57 result[name] = QUrlQuery( match.captured( name ) ).toString();
88 for (
auto it = constContentTypes.constBegin();
89 it != constContentTypes.constEnd(); ++it )
91 const auto constValues = it.value();
92 for (
const auto &value : constValues )
94 if ( accept.contains( value, Qt::CaseSensitivity::CaseInsensitive ) )
109 switch ( contentType )
112 data[
"handler"] =
schema( context );
113 if ( !htmlMetadata.is_null() )
115 data[
"metadata"] = htmlMetadata;
142 if ( match.captured().count() > 0 )
144 url.setPath( urlBasePath + match.captured( 0 ) );
148 url.setPath( urlBasePath );
152 const auto suffixLength { QFileInfo( url.path() ).suffix().length() };
153 if ( suffixLength > 0 )
155 auto path { url.path() };
156 path.truncate(
path.length() - ( suffixLength + 1 ) );
161 url.setPath( url.path() + extraPath );
165 if ( !extension.isEmpty() )
168 QString
path { url.path() };
169 while (
path.endsWith(
'/' ) )
173 url.setPath(
path +
'.' + extension );
183 QDateTime time { QDateTime::currentDateTime() };
184 time.setTimeSpec( Qt::TimeSpec::UTC );
185 data[
"timeStamp"] = time.toString( Qt::DateFormat::ISODate ).toStdString();
209 {
"title", title !=
"" ? title :
linkTitle() },
217 json
links = json::array();
218 const QList<QgsServerOgcApi::ContentType> constCts {
contentTypes() };
219 for (
const auto &ct : constCts )
233 const QRegularExpressionMatch match {
path().match( context.
request()->
url().path() ) };
234 if ( !match.hasMatch() )
238 const QString collectionId { match.captured( QStringLiteral(
"collectionId" ) ) };
253 path += QLatin1String(
"/ogc/templates" );
257 path += QLatin1String(
".html" );
264 context.
response()->
setHeader( QStringLiteral(
"Content-Type" ), QStringLiteral(
"text/html" ) );
266 if ( !QFile::exists(
path ) )
273 if ( !f.open( QFile::ReadOnly | QFile::Text ) )
282 QFileInfo pathInfo {
path };
283 Environment env { QString( pathInfo.dir().path() + QDir::separator() ).toStdString() };
286 env.add_callback(
"json_dump", 0, [=]( Arguments & ) {
291 env.add_callback(
"path_append", 1, [=]( Arguments &args ) {
292 auto url { context.
request()->url() };
293 QFileInfo fi { url.path() };
294 auto suffix { fi.suffix() };
295 auto fName { fi.filePath() };
296 if ( !suffix.isEmpty() )
298 fName.chop( suffix.length() + 1 );
301 while ( fName.endsWith(
'/' ) )
305 fName +=
'/' + QString::fromStdString( args.at( 0 )->get<std::string>() );
306 if ( !suffix.isEmpty() )
308 fName +=
'.' + suffix;
311 url.setPath( fi.filePath() );
312 return url.toString().toStdString();
316 env.add_callback(
"path_chomp", 1, [=]( Arguments &args ) {
317 QUrl url { QString::fromStdString( args.at( 0 )->get<std::string>() ) };
318 QFileInfo fi { url.path() };
319 auto suffix { fi.suffix() };
320 auto fName { fi.filePath() };
321 fName.chop( suffix.length() + 1 );
323 const thread_local QRegularExpression segmentRx( R
"raw(\/[^/]+$)raw" );
324 fName = fName.replace( segmentRx, QString() );
325 if ( !suffix.isEmpty() )
327 fName +=
'.' + suffix;
330 url.setPath( fi.filePath() );
331 return url.toString().toStdString();
336 env.add_callback(
"links_filter", 3, [=]( Arguments &args ) {
337 json
links = args.at( 0 )->get<json>();
338 if ( !
links.is_array() )
340 links = json::array();
342 std::string key { args.at( 1 )->get<std::string>() };
343 std::string value { args.at( 2 )->get<std::string>() };
344 json result = json::array();
345 for (
const auto &l :
links )
347 if ( l[key] == value )
349 result.push_back( l );
356 env.add_callback(
"content_type_name", 1, [=]( Arguments &args ) {
362 env.add_callback(
"nl2br", 1, [=]( Arguments &args ) {
363 QString text { QString::fromStdString( args.at( 0 )->get<std::string>() ) };
364 return text.replace(
'\n', QLatin1String(
"<br>" ) ).toStdString();
370 env.add_callback(
"component_parameter", 1, [=]( Arguments &args ) {
371 json ret = json::array();
372 json ref = args.at( 0 )->get<json>();
373 if ( !ref.is_object() )
379 QString name = QString::fromStdString( ref[
"$ref"] );
380 name = name.split(
'/' ).last();
381 ret.push_back( data[
"components"][
"parameters"][name.toStdString()] );
383 catch ( std::exception & )
392 env.add_callback(
"static", 1, [=]( Arguments &args ) {
393 auto asset( args.at( 0 )->get<std::string>() );
396 if ( matchedPath ==
'/' )
400 return matchedPath.toStdString() +
"/static/" + asset;
405 env.add_callback(
"starts_with", 2, []( Arguments &args ) {
406 return string_view::starts_with( args.at( 0 )->get<std::string_view>(), args.at( 1 )->get<std::string_view>() );
409 context.
response()->
write( env.render_file( pathInfo.fileName().toStdString(), data ) );
411 catch ( std::exception &e )
422 bool found {
false };
424 const QString extension { QFileInfo( request->
url().path() ).suffix().toUpper() };
425 if ( !extension.isEmpty() )
427 static QMetaEnum metaEnum { QMetaEnum::fromType<QgsServerOgcApi::ContentType>() };
429 const int ct { metaEnum.keyToValue( extension.toLocal8Bit().constData(), &ok ) };
441 const QString accept { request->
header( QStringLiteral(
"Accept" ) ) };
442 if ( !found && !accept.isEmpty() )
445 if ( !ctFromAccept.isEmpty() )
448 auto it = constContentTypes.constBegin();
449 while ( !found && it != constContentTypes.constEnd() )
451 int idx = it.value().indexOf( ctFromAccept );
469 bool found {
false };
472 const QList<QgsServerOgcApi::ContentType> constCt {
contentTypes() };
473 for (
const auto &ct : constCt )
495 QString
path { url.path() };
496 const QFileInfo fi {
path };
497 const QString suffix { fi.suffix() };
498 if ( !suffix.isEmpty() )
500 path.chop( suffix.length() + 1 );
502 while (
path.endsWith(
'/' ) )
506 const thread_local QRegularExpression re( R
"raw(\/[^/]+$)raw" );
507 for (
int i = 0; i < levels; i++ )
509 path =
path.replace( re, QString() );
512 QUrlQuery query( result );
513 QList<QPair<QString, QString>> qi;
514 const auto constItems { query.queryItems() };
515 for (
const auto &i : constItems )
517 if ( i.first.compare( QStringLiteral(
"MAP" ), Qt::CaseSensitivity::CaseInsensitive ) == 0 )
523 if ( !
path.endsWith(
'/' ) )
527 QUrlQuery resultQuery;
528 resultQuery.setQueryItems( qi );
529 result.setQuery( resultQuery );
530 result.setPath(
path );
531 return result.toString();
537 if ( mapLayers.count() != 1 )
539 throw QgsServerApiNotFoundError( QStringLiteral(
"Collection with given id (%1) was not found or multiple matches were found" ).arg( collectionId ) );
541 return mapLayers.first();
546 static json defRes = {
547 {
"description",
"An error occurred." },
548 {
"content", { {
"application/json", { {
"schema", { {
"$ref",
"#/components/schemas/exception" } } } } }, {
"text/html", { {
"schema", { {
"type",
"string" } } } } } }
561 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)
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.
The QgsServerApiContext class encapsulates the resources for a particular client request: the 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.
this method is not yet implemented
virtual QgsServerSettings * serverSettings()=0
Returns the server settings.
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.
void jsonDump(json &data, const QgsServerApiContext &context, const QString &contentType=QStringLiteral("application/json")) const
Writes data to the context response stream as JSON (indented if debug is active), an optional content...
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.
QgsServerRequest Class defining request interface passed to services QgsService::executeRequest() met...
virtual QString header(const QString &name) const
Returns the header value.
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 data sets.