32 #include "nlohmann/json.hpp" 33 #include "inja/inja.hpp" 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() ).completeSuffix().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 += QStringLiteral(
"/ogc/templates" );
263 path += QString::fromStdString( operationId() );
264 path += QStringLiteral(
".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 { ( pathInfo.dir().path() + QDir::separator() ).toStdString() };
293 env.add_callback(
"json_dump", 0, [ = ]( Arguments & )
299 env.add_callback(
"path_append", 1, [ = ]( Arguments & args )
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>( ) );
408 return context.
matchedPath().toStdString() +
"/static/" + asset;
411 context.
response()->
write( env.render_file( pathInfo.fileName().toStdString(), data ) );
413 catch ( std::exception &e )
424 bool found {
false };
426 const QString extension { QFileInfo( request->
url().path() ).completeSuffix().toUpper() };
427 if ( ! extension.isEmpty() )
429 static QMetaEnum metaEnum { QMetaEnum::fromType<QgsServerOgcApi::ContentType>() };
431 const int ct { metaEnum.keyToValue( extension.toLocal8Bit().constData(), &ok ) };
443 const QString accept { request->
header( QStringLiteral(
"Accept" ) ) };
444 if ( ! found && ! accept.isEmpty() )
446 const QString ctFromAccept { contentTypeForAccept( accept ) };
447 if ( ! ctFromAccept.isEmpty() )
452 int idx = it.value().indexOf( ctFromAccept );
467 if ( ! contentTypes().contains( result ) )
470 bool found {
false };
473 const QList<QgsServerOgcApi::ContentType> constCt { contentTypes() };
474 for (
const auto &ct : constCt )
496 QString path { url.path() };
497 const QFileInfo fi { path };
498 const QString suffix { fi.suffix() };
499 if ( ! suffix.isEmpty() )
501 path.chop( suffix.length() + 1 );
503 while ( path.endsWith(
'/' ) )
507 QRegularExpression re( R
"raw(\/[^/]+$)raw" ); 508 for (
int i = 0; i < levels ; i++ )
510 path = path.replace( re, QString() );
513 QUrlQuery query( result );
514 QList<QPair<QString, QString> > qi;
515 const auto constItems { query.queryItems( ) };
516 for (
const auto &i : constItems )
518 if ( i.first.compare( QStringLiteral(
"MAP" ), Qt::CaseSensitivity::CaseInsensitive ) == 0 )
524 if ( ! path.endsWith(
'/' ) )
528 QUrlQuery resultQuery;
529 resultQuery.setQueryItems( qi );
530 result.setQuery( resultQuery );
531 result.setPath( path );
532 return result.toString();
538 if ( mapLayers.count() != 1 )
540 throw QgsServerApiNotFoundError( QStringLiteral(
"Collection with given id (%1) was not found or multiple matches were found" ).arg( collectionId ) );
542 return mapLayers.first();
549 {
"description",
"An error occurred." },
553 "application/json", {
556 {
"$ref",
"#/components/schemas/exception" }
583 mContentTypes.clear();
584 for (
const int &i : qgis::as_const( contentTypes ) )
586 mContentTypes.push_back( static_cast<QgsServerOgcApi::ContentType>( i ) );
592 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.