32 #include "nlohmann/json.hpp"
33 #include "inja/inja.hpp"
35 using namespace nlohmann;
43 const auto constParameters { parameters( context ) };
44 for (
const auto &p : constParameters )
47 result[p.name()] = p.value( context );
50 const auto match { path().match( sanitizedPath ) };
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 const auto constContentTypes( contentTypes() );
72 return constContentTypes.size() > 0 ? constContentTypes.first() : QgsServerOgcApi::ContentType::JSON;
89 for (
auto it = constContentTypes.constBegin();
90 it != constContentTypes.constEnd(); ++it )
92 const auto constValues = it.value();
93 for (
const auto &value : constValues )
95 if ( accept.contains( value, Qt::CaseSensitivity::CaseInsensitive ) )
103 QStringLiteral(
"Server" ),
104 Qgis::MessageLevel::Info );
112 switch ( contentType )
114 case QgsServerOgcApi::ContentType::HTML:
115 data[
"handler"] = schema( context );
116 if ( ! htmlMetadata.is_null() )
118 data[
"metadata"] = htmlMetadata;
120 htmlDump( data, context );
122 case QgsServerOgcApi::ContentType::GEOJSON:
123 case QgsServerOgcApi::ContentType::JSON:
124 case QgsServerOgcApi::ContentType::OPENAPI3:
127 case QgsServerOgcApi::ContentType::XML:
145 if ( match.captured().count() > 0 )
147 url.setPath( urlBasePath + match.captured( 0 ) );
151 url.setPath( urlBasePath );
155 const auto suffixLength { QFileInfo( url.path() ).suffix().length() };
156 if ( suffixLength > 0 )
158 auto path {url.path()};
159 path.truncate( path.length() - ( suffixLength + 1 ) );
164 url.setPath( url.path() + extraPath );
168 if ( ! extension.isEmpty() )
171 QString path { url.path() };
172 while ( path.endsWith(
'/' ) )
176 url.setPath( path +
'.' + extension );
187 QDateTime time { QDateTime::currentDateTime() };
188 time.setTimeSpec( Qt::TimeSpec::UTC );
189 data[
"timeStamp"] = time.toString( Qt::DateFormat::ISODate ).toStdString() ;
211 "href", href( context,
"/",
216 {
"title", title !=
"" ? title : linkTitle() },
224 json links = json::array();
225 const QList<QgsServerOgcApi::ContentType> constCts { contentTypes() };
226 for (
const auto &ct : constCts )
228 links.push_back( link( context, ( ct == currentCt ? QgsServerOgcApi::Rel::self :
229 QgsServerOgcApi::Rel::alternate ), ct,
242 const QRegularExpressionMatch match { path().match( context.
request()->
url().path( ) ) };
243 if ( ! match.hasMatch() )
247 const QString collectionId { match.captured( QStringLiteral(
"collectionId" ) ) };
249 return layerFromCollectionId( context, collectionId );
263 path += QLatin1String(
"/ogc/templates" );
266 path += QString::fromStdString( operationId() );
267 path += QLatin1String(
".html" );
274 context.
response()->
setHeader( QStringLiteral(
"Content-Type" ), QStringLiteral(
"text/html" ) );
275 auto path { templatePath( context ) };
276 if ( ! QFile::exists( path ) )
278 QgsMessageLog::logMessage( QStringLiteral(
"Template not found error: %1" ).arg( path ), QStringLiteral(
"Server" ), Qgis::MessageLevel::Critical );
283 if ( ! f.open( QFile::ReadOnly | QFile::Text ) )
285 QgsMessageLog::logMessage( QStringLiteral(
"Could not open template file: %1" ).arg( path ), QStringLiteral(
"Server" ), Qgis::MessageLevel::Critical );
292 QFileInfo pathInfo { path };
293 Environment env { QString( pathInfo.dir().path() + QDir::separator() ).toStdString() };
296 env.add_callback(
"json_dump", 0, [ = ]( Arguments & )
302 env.add_callback(
"path_append", 1, [ = ]( Arguments & args )
304 auto url { context.
request()->url() };
305 QFileInfo fi{ url.path() };
306 auto suffix { fi.suffix() };
307 auto fName { fi.filePath()};
308 if ( !suffix.isEmpty() )
310 fName.chop( suffix.length() + 1 );
313 while ( fName.endsWith(
'/' ) )
317 fName +=
'/' + QString::fromStdString( args.at( 0 )->get<std::string>( ) );
318 if ( !suffix.isEmpty() )
320 fName +=
'.' + suffix;
323 url.setPath( fi.filePath() );
324 return url.toString().toStdString();
328 env.add_callback(
"path_chomp", 1, [ = ]( Arguments & args )
330 QUrl url { QString::fromStdString( args.at( 0 )->get<std::string>( ) ) };
331 QFileInfo fi{ url.path() };
332 auto suffix { fi.suffix() };
333 auto fName { fi.filePath()};
334 fName.chop( suffix.length() + 1 );
336 fName = fName.replace( QRegularExpression( R
"raw(\/[^/]+$)raw" ), QString() );
337 if ( !suffix.isEmpty() )
339 fName +=
'.' + suffix;
342 url.setPath( fi.filePath() );
343 return url.toString().toStdString();
348 env.add_callback(
"links_filter", 3, [ = ]( Arguments & args )
350 json links = args.at( 0 )->get<json>( );
351 if ( ! links.is_array() )
353 links = json::array();
355 std::string key { args.at( 1 )->get<std::string>( ) };
356 std::string value { args.at( 2 )->get<std::string>( ) };
357 json result = json::array();
358 for (
const auto &l : links )
360 if ( l[key] == value )
362 result.push_back( l );
369 env.add_callback(
"content_type_name", 1, [ = ]( Arguments & args )
376 env.add_callback(
"nl2br", 1, [ = ]( Arguments & args )
378 QString text { QString::fromStdString( args.at( 0 )->get<std::string>( ) ) };
379 return text.replace(
'\n', QLatin1String(
"<br>" ) ).toStdString();
385 env.add_callback(
"component_parameter", 1, [ = ]( Arguments & args )
387 json ret = json::array();
388 json ref = args.at( 0 )->get<json>( );
389 if ( ! ref.is_object() )
395 QString name = QString::fromStdString( ref[
"$ref"] );
396 name = name.split(
'/' ).last();
397 ret.push_back( data[
"components"][
"parameters"][name.toStdString()] );
399 catch ( std::exception & )
408 env.add_callback(
"static", 1, [ = ]( Arguments & args )
410 auto asset( args.at( 0 )->get<std::string>( ) );
413 if ( matchedPath ==
'/' )
417 return matchedPath.toStdString() +
"/static/" + asset;
420 context.
response()->
write( env.render_file( pathInfo.fileName().toStdString(), data ) );
422 catch ( std::exception &e )
424 QgsMessageLog::logMessage( QStringLiteral(
"Error parsing template file: %1 - %2" ).arg( path, e.what() ), QStringLiteral(
"Server" ), Qgis::MessageLevel::Critical );
433 bool found {
false };
435 const QString extension { QFileInfo( request->
url().path() ).suffix().toUpper() };
436 if ( ! extension.isEmpty() )
438 static QMetaEnum metaEnum { QMetaEnum::fromType<QgsServerOgcApi::ContentType>() };
440 const int ct { metaEnum.keyToValue( extension.toLocal8Bit().constData(), &ok ) };
448 QgsMessageLog::logMessage( QStringLiteral(
"The client requested an unsupported extension: %1" ).arg( extension ), QStringLiteral(
"Server" ), Qgis::MessageLevel::Warning );
452 const QString accept { request->
header( QStringLiteral(
"Accept" ) ) };
453 if ( ! found && ! accept.isEmpty() )
455 const QString ctFromAccept { contentTypeForAccept( accept ) };
456 if ( ! ctFromAccept.isEmpty() )
459 auto it = constContentTypes.constBegin();
460 while ( ! found && it != constContentTypes.constEnd() )
462 int idx = it.value().indexOf( ctFromAccept );
473 QgsMessageLog::logMessage( QStringLiteral(
"The client requested an unsupported content type in Accept header: %1" ).arg( accept ), QStringLiteral(
"Server" ), Qgis::MessageLevel::Warning );
477 if ( ! contentTypes().contains( result ) )
480 bool found {
false };
483 const QList<QgsServerOgcApi::ContentType> constCt { contentTypes() };
484 for (
const auto &ct : constCt )
506 QString path { url.path() };
507 const QFileInfo fi { path };
508 const QString suffix { fi.suffix() };
509 if ( ! suffix.isEmpty() )
511 path.chop( suffix.length() + 1 );
513 while ( path.endsWith(
'/' ) )
517 QRegularExpression re( R
"raw(\/[^/]+$)raw" );
518 for (
int i = 0; i < levels ; i++ )
520 path = path.replace( re, QString() );
523 QUrlQuery query( result );
524 QList<QPair<QString, QString> > qi;
525 const auto constItems { query.queryItems( ) };
526 for (
const auto &i : constItems )
528 if ( i.first.compare( QStringLiteral(
"MAP" ), Qt::CaseSensitivity::CaseInsensitive ) == 0 )
534 if ( ! path.endsWith(
'/' ) )
538 QUrlQuery resultQuery;
539 resultQuery.setQueryItems( qi );
540 result.setQuery( resultQuery );
541 result.setPath( path );
542 return result.toString();
548 if ( mapLayers.count() != 1 )
550 throw QgsServerApiNotFoundError( QStringLiteral(
"Collection with given id (%1) was not found or multiple matches were found" ).arg( collectionId ) );
552 return mapLayers.first();
559 {
"description",
"An error occurred." },
563 "application/json", {
566 {
"$ref",
"#/components/schemas/exception" }
593 mContentTypes.clear();
594 for (
const int &i : std::as_const( contentTypes ) )
602 mContentTypes = contentTypes;