32 #include "nlohmann/json.hpp"    33 #include "inja/inja.hpp"    43   QVariantList positional;
    44   const auto constParameters { parameters( context ) };
    45   for ( 
const auto &p : constParameters )
    48     result[p.name()] = p.value( context );
    50   const auto match { path().match( context.
request()->
url().toString() ) };
    51   if ( match.hasMatch() )
    53     const auto constNamed { path().namedCaptureGroups() };
    55     for ( 
const auto &name : constNamed )
    57       if ( ! name.isEmpty() )
    58         result[name] = QUrlQuery( match.captured( name ) ).toString() ;
    71   return contentTypes().size() > 0 ? contentTypes().first() : QgsServerOgcApi::ContentType::JSON;
    90     const auto constValues = it.value();
    91     for ( 
const auto &value : constValues )
    93       if ( accept.contains( value, Qt::CaseSensitivity::CaseInsensitive ) )
   101                              QStringLiteral( 
"Server" ),
   110   switch ( contentType )
   112     case QgsServerOgcApi::ContentType::HTML:
   113       data[
"handler"] = schema( context );
   114       if ( ! htmlMetadata.is_null() )
   116         data[
"metadata"] = htmlMetadata;
   118       htmlDump( data, context );
   120     case QgsServerOgcApi::ContentType::GEOJSON:
   121     case QgsServerOgcApi::ContentType::JSON:
   122     case QgsServerOgcApi::ContentType::OPENAPI3:
   139   const auto match { path().match( url.path() ) };
   140   if ( match.captured().count() > 0 )
   142     url.setPath( urlBasePath + match.captured( 0 ) );
   146     url.setPath( urlBasePath );
   150   const auto suffixLength { QFileInfo( url.path() ).completeSuffix().length() };
   151   if ( suffixLength > 0 )
   153     auto path {url.path()};
   154     path.truncate( path.length() - ( suffixLength + 1 ) );
   159   url.setPath( url.path() + extraPath );
   163   if ( ! extension.isEmpty() )
   166     QString path { url.path() };
   167     while ( path.endsWith( 
'/' ) )
   171     url.setPath( path + 
'.' + extension );
   182     QDateTime time { QDateTime::currentDateTime() };
   183     time.setTimeSpec( Qt::TimeSpec::UTC );
   184     data[
"timeStamp"] = time.toString( Qt::DateFormat::ISODate ).toStdString() ;
   206       "href", href( context, 
"/",
   211     { 
"title", title != 
"" ? title : linkTitle() },
   219   json links = json::array();
   220   const QList<QgsServerOgcApi::ContentType> constCts { contentTypes() };
   221   for ( 
const auto &ct : constCts )
   223     links.push_back( link( context, ( ct == currentCt ? QgsServerOgcApi::Rel::self :
   224                                       QgsServerOgcApi::Rel::alternate ), ct,
   237   const QRegularExpressionMatch match { path().match( context.
request()->
url().path( ) ) };
   238   if ( ! match.hasMatch() )
   242   const QString collectionId { match.captured( QStringLiteral( 
"collectionId" ) ) };
   244   return layerFromCollectionId( context, collectionId );
   258   path += QStringLiteral( 
"/ogc/templates" );
   261   path += QString::fromStdString( operationId() );
   262   path += QStringLiteral( 
".html" );
   269   context.
response()->
setHeader( QStringLiteral( 
"Content-Type" ), QStringLiteral( 
"text/html" ) );
   270   auto path { templatePath( context ) };
   271   if ( ! QFile::exists( path ) )
   278   if ( ! f.open( QFile::ReadOnly | QFile::Text ) )
   287     QFileInfo pathInfo { path };
   288     Environment env { ( pathInfo.dir().path() + QDir::separator() ).toStdString() };
   291     env.add_callback( 
"json_dump", 0, [ = ]( Arguments & )
   297     env.add_callback( 
"path_append", 1, [ = ]( Arguments & args )
   300       QFileInfo fi{ url.path() };
   301       auto suffix { fi.suffix() };
   302       auto fName { fi.filePath()};
   303       fName.chop( suffix.length() + 1 );
   304       fName += 
'/' + QString::number( args.at( 0 )->get<
QgsFeatureId>( ) );
   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 )
   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       fName = fName.replace( QRegularExpression( R
"raw(\/[^/]+$)raw" ), 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 )
   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 )
   364     env.add_callback( 
"static", 1, [ = ]( Arguments & args )
   366       auto asset( args.at( 0 )->get<std::string>( ) );
   367       return context.
matchedPath().toStdString() + 
"/static/" + asset;
   370     context.
response()->
write( env.render_file( pathInfo.fileName().toStdString(), data ) );
   372   catch ( std::exception &e )
   383   bool found { 
false };
   385   const QString extension { QFileInfo( request->
url().path() ).completeSuffix().toUpper() };
   386   if ( ! extension.isEmpty() )
   388     static QMetaEnum metaEnum { QMetaEnum::fromType<QgsServerOgcApi::ContentType>() };
   390     const int ct  { metaEnum.keyToValue( extension.toLocal8Bit().constData(), &ok ) };
   402   const QString accept { request->
header( QStringLiteral( 
"Accept" ) ) };
   403   if ( ! found && ! accept.isEmpty() )
   405     const QString ctFromAccept { contentTypeForAccept( accept ) };
   406     if ( ! ctFromAccept.isEmpty() )
   411         int idx = it.value().indexOf( ctFromAccept );
   426   if ( ! contentTypes().contains( result ) )
   429     bool found { 
false };
   432       const QList<QgsServerOgcApi::ContentType> constCt { contentTypes() };
   433       for ( 
const auto &ct : constCt )
   455   QString path { url.path() };
   456   const QFileInfo fi { path };
   457   const QString suffix { fi.suffix() };
   458   if ( ! suffix.isEmpty() )
   460     path.chop( suffix.length() + 1 );
   462   while ( path.endsWith( 
'/' ) )
   466   QRegularExpression re( R
"raw(\/[^/]+$)raw" );   467   for ( 
int i = 0; i < levels ; i++ )
   469     path = path.replace( re, QString() );
   472   QList<QPair<QString, QString> > qi;
   473   const auto constItems { result.queryItems( ) };
   474   for ( 
const auto &i : constItems )
   476     if ( i.first.compare( QStringLiteral( 
"MAP" ), Qt::CaseSensitivity::CaseInsensitive ) == 0 )
   481   result.setQueryItems( qi );
   482   result.setPath( path );
   483   return result.toString();
   489   if ( mapLayers.count() != 1 )
   491     throw QgsServerApiNotFoundError( QStringLiteral( 
"Collection with given id (%1) was not found or multiple matches were found" ).arg( collectionId ) );
   493   return mapLayers.first();
   500     { 
"description", 
"An error occurred." },
   504           "application/json", {
   507                 { 
"$ref", 
"#/components/schemas/exception" }
   534   mContentTypes.clear();
   535   for ( 
const int &i : qgis::as_const( contentTypes ) )
   537     mContentTypes.push_back( static_cast<QgsServerOgcApi::ContentType>( i ) );
   543   mContentTypes = contentTypes;
 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...
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...
static json jsonFromVariant(const QVariant &v)
Converts a QVariant v to a json object. 
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...
Not found error API exception. 
virtual QgsServerSettings * serverSettings()=0
Returns the server settings. 
const QgsProject * project() const
Returns the (possibly NULL) project. 
QString header(const QString &name) const
Returns the header value. 
void htmlDump(const json &data, const QgsServerApiContext &context) const
Writes data as HTML to the response stream in context using a template. 
static QString contentTypeToString(const QgsServerOgcApi::ContentType &ct)
Returns the string representation of a ct (Content-Type) attribute. 
QgsServerResponse * response() const
Returns the server response object. 
QString apiRootPath() const
Returns the API root path. 
QList< QgsMapLayer * > mapLayersByShortName(const QString &shortName) const
Retrieves a list of matching registered layers by layer shortName. 
static const QHash< QgsServerOgcApi::ContentType, QList< QgsServerOgcApi::ContentType > > contentTypeAliases()
Returns contenType specializations (e.g. 
QgsServerOgcApi::ContentType contentTypeFromRequest(const QgsServerRequest *request) const
Returns the content type from the request. 
static std::string relToString(const QgsServerOgcApi::Rel &rel)
Returns the string representation of rel attribute. 
static QgsServerOgcApi::ContentType contenTypeFromExtension(const std::string &extension)
Returns the Content-Type value corresponding to extension. 
void setContentTypesInt(const QList< int > &contentTypes)
Set the content types to contentTypes. 
virtual void write(const QString &data)
Write string This is a convenient method that will write directly to the underlying I/O device...
Internal server error API exception. 
static const QMap< QgsServerOgcApi::ContentType, QStringList > contentTypeMimes()
Returns a map of contentType => list of mime types. 
QgsServerInterface * serverInterface() const
Returns the server interface. 
ContentType
Media types used for content negotiation, insert more specific first. 
this method is not yet implemented 
static json defaultResponse()
Returns the defaultResponse as JSON. 
virtual void setStatusCode(int code)=0
Set the http status code. 
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. 
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. 
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...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary). 
void setContentTypes(const QList< QgsServerOgcApi::ContentType > &contentTypes)
Set the content types to contentTypes. 
virtual QgsServerOgcApi::ContentType defaultContentType() const
Returns the default response content type in case the client did not specifically ask for any particu...
json links(const QgsServerApiContext &context) const
Returns all the links for the given request context. 
static QUrl sanitizeUrl(const QUrl &url)
Returns a sanitized url with extra slashes removed. 
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...
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 ...
The QgsServerApiContext class encapsulates the resources for a particular client request: the request...
QgsServerRequest Class defining request interface passed to services QgsService::executeRequest() met...
Bad request error API exception. 
virtual QVariantMap values(const QgsServerApiContext &context) const SIP_THROW(QgsServerApiBadRequestException)
Analyzes the incoming request context and returns the validated parameter map, throws QgsServerApiBad...
QList< QgsServerOgcApi::ContentType > contentTypes() const
Returns the list of content types this handler can serve, default to JSON and HTML. 
virtual ~QgsServerOgcApiHandler()
QString apiResourcesDirectory() const
Returns the server-wide base directory where HTML templates and static assets (e.g. 
static QString contentTypeToExtension(const QgsServerOgcApi::ContentType &ct)
Returns the file extension for a ct (Content-Type). 
const QString matchedPath() const
Returns the initial part of the incoming request URL path that matches the API root path...
virtual void handleRequest(const QgsServerApiContext &context) const SIP_THROW(QgsServerApiBadRequestException)
Handles the request within its context. 
json jsonTags() const
Returns tags as JSON. 
QString contentTypeForAccept(const QString &accept) const
Looks for the first ContentType match in the accept header and returns its mime type, returns an empty string if there are not matches. 
QgsVectorLayer * layerFromContext(const QgsServerApiContext &context) const
Returns a vector layer instance from the "collectionId" parameter of the path in the given context...
Represents a vector layer which manages a vector based data sets. 
virtual const QString templatePath(const QgsServerApiContext &context) const
Returns the HTML template path for the handler in the given context. 
static QgsVectorLayer * layerFromCollectionId(const QgsServerApiContext &context, const QString &collectionId)
Returns a vector layer from the collectionId in the given context. 
virtual json schema(const QgsServerApiContext &context) const
Returns handler information from the context for the OPENAPI description (id, description and other m...
const QgsServerRequest * request() const
Returns the server request object.