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 );
49 const auto match { path().match( context.
request()->
url().toString() ) };
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() ;
70 return contentTypes().size() > 0 ? contentTypes().first() : QgsServerOgcApi::ContentType::JSON;
89 const auto constValues = it.value();
90 for (
const auto &value : constValues )
92 if ( accept.contains( value, Qt::CaseSensitivity::CaseInsensitive ) )
100 QStringLiteral(
"Server" ),
109 switch ( contentType )
111 case QgsServerOgcApi::ContentType::HTML:
112 data[
"handler"] = schema( context );
113 if ( ! htmlMetadata.is_null() )
115 data[
"metadata"] = htmlMetadata;
117 htmlDump( data, context );
119 case QgsServerOgcApi::ContentType::GEOJSON:
120 case QgsServerOgcApi::ContentType::JSON:
121 case QgsServerOgcApi::ContentType::OPENAPI3:
124 case QgsServerOgcApi::ContentType::XML:
141 const auto match { path().match( url.path() ) };
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 );
184 QDateTime time { QDateTime::currentDateTime() };
185 time.setTimeSpec( Qt::TimeSpec::UTC );
186 data[
"timeStamp"] = time.toString( Qt::DateFormat::ISODate ).toStdString() ;
208 "href", href( context,
"/",
213 {
"title", title !=
"" ? title : linkTitle() },
221 json links = json::array();
222 const QList<QgsServerOgcApi::ContentType> constCts { contentTypes() };
223 for (
const auto &ct : constCts )
225 links.push_back( link( context, ( ct == currentCt ? QgsServerOgcApi::Rel::self :
226 QgsServerOgcApi::Rel::alternate ), ct,
239 const QRegularExpressionMatch match { path().match( context.
request()->
url().path( ) ) };
240 if ( ! match.hasMatch() )
244 const QString collectionId { match.captured( QStringLiteral(
"collectionId" ) ) };
246 return layerFromCollectionId( context, collectionId );
260 path += QLatin1String(
"/ogc/templates" );
263 path += QString::fromStdString( operationId() );
264 path += QLatin1String(
".html" );
271 context.
response()->
setHeader( QStringLiteral(
"Content-Type" ), QStringLiteral(
"text/html" ) );
272 auto path { templatePath( context ) };
273 if ( ! QFile::exists( path ) )
280 if ( ! f.open( QFile::ReadOnly | QFile::Text ) )
289 QFileInfo pathInfo { path };
290 Environment env { QString( pathInfo.dir().path() + QDir::separator() ).toStdString() };
293 env.add_callback(
"json_dump", 0, [ = ]( Arguments & )
299 env.add_callback(
"path_append", 1, [ = ]( Arguments & args )
301 auto url { context.
request()->url() };
302 QFileInfo fi{ url.path() };
303 auto suffix { fi.suffix() };
304 auto fName { fi.filePath()};
305 if ( !suffix.isEmpty() )
307 fName.chop( suffix.length() + 1 );
310 while ( fName.endsWith(
'/' ) )
314 fName +=
'/' + QString::number( args.at( 0 )->get<
QgsFeatureId>( ) );
315 if ( !suffix.isEmpty() )
317 fName +=
'.' + suffix;
320 url.setPath( fi.filePath() );
321 return url.toString().toStdString();
325 env.add_callback(
"path_chomp", 1, [ = ]( Arguments & args )
327 QUrl url { QString::fromStdString( args.at( 0 )->get<std::string>( ) ) };
328 QFileInfo fi{ url.path() };
329 auto suffix { fi.suffix() };
330 auto fName { fi.filePath()};
331 fName.chop( suffix.length() + 1 );
333 fName = fName.replace( QRegularExpression( R
"raw(\/[^/]+$)raw" ), QString() );
334 if ( !suffix.isEmpty() )
336 fName +=
'.' + suffix;
339 url.setPath( fi.filePath() );
340 return url.toString().toStdString();
345 env.add_callback(
"links_filter", 3, [ = ]( Arguments & args )
347 json links = args.at( 0 )->get<json>( );
348 if ( ! links.is_array() )
350 links = json::array();
352 std::string key { args.at( 1 )->get<std::string>( ) };
353 std::string value { args.at( 2 )->get<std::string>( ) };
354 json result = json::array();
355 for (
const auto &l : links )
357 if ( l[key] == value )
359 result.push_back( l );
366 env.add_callback(
"content_type_name", 1, [ = ]( Arguments & args )
373 env.add_callback(
"nl2br", 1, [ = ]( Arguments & args )
375 QString text { QString::fromStdString( args.at( 0 )->get<std::string>( ) ) };
376 return text.replace(
'\n', QLatin1String(
"<br>" ) ).toStdString();
382 env.add_callback(
"component_parameter", 1, [ = ]( Arguments & args )
384 json ret = json::array();
385 json ref = args.at( 0 )->get<json>( );
386 if ( ! ref.is_object() )
392 QString name = QString::fromStdString( ref[
"$ref"] );
393 name = name.split(
'/' ).last();
394 ret.push_back( data[
"components"][
"parameters"][name.toStdString()] );
396 catch ( std::exception & )
405 env.add_callback(
"static", 1, [ = ]( Arguments & args )
407 auto asset( args.at( 0 )->get<std::string>( ) );
410 if ( matchedPath ==
'/' )
414 return matchedPath.toStdString() +
"/static/" + asset;
417 context.
response()->
write( env.render_file( pathInfo.fileName().toStdString(), data ) );
419 catch ( std::exception &e )
430 bool found {
false };
432 const QString extension { QFileInfo( request->
url().path() ).suffix().toUpper() };
433 if ( ! extension.isEmpty() )
435 static QMetaEnum metaEnum { QMetaEnum::fromType<QgsServerOgcApi::ContentType>() };
437 const int ct { metaEnum.keyToValue( extension.toLocal8Bit().constData(), &ok ) };
449 const QString accept { request->
header( QStringLiteral(
"Accept" ) ) };
450 if ( ! found && ! accept.isEmpty() )
452 const QString ctFromAccept { contentTypeForAccept( accept ) };
453 if ( ! ctFromAccept.isEmpty() )
458 int idx = it.value().indexOf( ctFromAccept );
473 if ( ! contentTypes().contains( result ) )
476 bool found {
false };
479 const QList<QgsServerOgcApi::ContentType> constCt { contentTypes() };
480 for (
const auto &ct : constCt )
502 QString path { url.path() };
503 const QFileInfo fi { path };
504 const QString suffix { fi.suffix() };
505 if ( ! suffix.isEmpty() )
507 path.chop( suffix.length() + 1 );
509 while ( path.endsWith(
'/' ) )
513 QRegularExpression re( R
"raw(\/[^/]+$)raw" );
514 for (
int i = 0; i < levels ; i++ )
516 path = path.replace( re, QString() );
519 QUrlQuery query( result );
520 QList<QPair<QString, QString> > qi;
521 const auto constItems { query.queryItems( ) };
522 for (
const auto &i : constItems )
524 if ( i.first.compare( QStringLiteral(
"MAP" ), Qt::CaseSensitivity::CaseInsensitive ) == 0 )
530 if ( ! path.endsWith(
'/' ) )
534 QUrlQuery resultQuery;
535 resultQuery.setQueryItems( qi );
536 result.setQuery( resultQuery );
537 result.setPath( path );
538 return result.toString();
544 if ( mapLayers.count() != 1 )
546 throw QgsServerApiNotFoundError( QStringLiteral(
"Collection with given id (%1) was not found or multiple matches were found" ).arg( collectionId ) );
548 return mapLayers.first();
555 {
"description",
"An error occurred." },
559 "application/json", {
562 {
"$ref",
"#/components/schemas/exception" }
589 mContentTypes.clear();
590 for (
const int &i : qgis::as_const( contentTypes ) )
598 mContentTypes = contentTypes;