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.