1 /***************************************************************************
2   qgsserverogcapi.h - QgsServerOgcApi
3 
4  ---------------------
5  begin                : 10.7.2019
6  copyright            : (C) 2019 by Alessandro Pasotti
7  email                : elpaso at itopen dot it
8  ***************************************************************************
9  *                                                                         *
10  *   This program is free software; you can redistribute it and/or modify  *
11  *   it under the terms of the GNU General Public License as published by  *
12  *   the Free Software Foundation; either version 2 of the License, or     *
13  *   (at your option) any later version.                                   *
14  *                                                                         *
15  ***************************************************************************/
16 #ifndef QGSSERVEROGCAPI_H
17 #define QGSSERVEROGCAPI_H
18 
19 #include "qgsserverapi.h"
20 #include "qgis_server.h"
21 
22 
23 class QgsServerOgcApiHandler;
24 
25 /**
26  * \ingroup server
27  * \brief QGIS Server OGC API endpoint. QgsServerOgcApi provides the foundation for
28  * the new generation of REST-API based OGC services (e.g. WFS3).
29  *
30  * This class can be used directly and configured by registering handlers
31  * as instances of QgsServerOgcApiHandler.
32  *
33  * \code{.py}
34  *
35  * class Handler1(QgsServerOgcApiHandler):
36  *   """A handler, see QgsServerOgcApiHandler for an example"""
37  *   ...
38  *
39  * h = Handler1()
40  * api = QgsServerOgcApi(serverInterface(), "/api1", "apione", "A firs API", "1.0")
41  * api.registerHandler(h)
42  * server.serverInterface().serviceRegistry().registerApi(api)
43  *
44  * \endcode
45  *
46  * \since QGIS 3.10
47  */
48 class SERVER_EXPORT QgsServerOgcApi : public QgsServerApi
49 {
50 
51     Q_GADGET
52 
53   public:
54 
55     // Note: non a scoped enum or qHash fails
56     //! Rel link types
57     enum Rel
58     {
59       // The following registered link relation types are used
60       alternate, //! Refers to a substitute for this context.
61       describedBy, //! Refers to a resource providing information about the link’s context.
62       collection, //! The target IRI points to a resource that is a member of the collection represented by the context IRI.
63       item, //! The target IRI points to a resource that is a member of the collection represented by the context IRI.
64       self, //! Conveys an identifier for the link’s context.
65       service_desc, //! Identifies service description for the context that is primarily intended for consumption by machines.
66       service_doc, //! Identifies service documentation for the context that is primarily intended for human consumption.
67       prev, //! Indicates that the link’s context is a part of a series, and that the previous in the series is the link targe
68       next, //! Indicates that the link’s context is a part of a series, and that the next in the series is the link target.
69       license, //! Refers to a license associated with this context.
70       // In addition the following link relation types are used for which no applicable registered link relation type could be identified:
71       items, //! Refers to a resource that is comprised of members of the collection represented by the link’s context.
72       conformance, //! The target IRI points to a resource which represents the collection resource for the context IRI.
73       data //! The target IRI points to resource data
74     };
75     Q_ENUM( Rel )
76 
77     // Note: cannot be a scoped enum because qHash does not support them
78     //! Media types used for content negotiation, insert more specific first
79     enum ContentType
80     {
81       GEOJSON,
82       OPENAPI3, //! "application/openapi+json;version=3.0"
83       JSON,
84       HTML,
85       XML
86     };
87     Q_ENUM( ContentType )
88 
89     /**
90      * QgsServerOgcApi constructor
91      * \param serverIface pointer to the server interface
92      * \param rootPath root path for this API (usually starts with a "/", e.g. "/wfs3")
93      * \param name API name
94      * \param description API description
95      * \param version API version
96      */
97     QgsServerOgcApi( QgsServerInterface *serverIface,
98                      const QString &rootPath,
99                      const QString &name,
100                      const QString &description = QString(),
101                      const QString &version = QString() );
102 
103     // QgsServerApi interface
name()104     const QString name() const override { return mName; }
description()105     const QString description() const override { return mDescription; }
version()106     const QString version() const override { return mVersion; }
rootPath()107     const QString rootPath() const override { return mRootPath ; }
108 
109     ~QgsServerOgcApi() override;
110 
111     /**
112      * Executes a request by passing the given \a context to the API handlers.
113      */
114     virtual void executeRequest( const QgsServerApiContext &context ) const override SIP_THROW( QgsServerApiBadRequestException ) SIP_VIRTUALERRORHANDLER( serverapi_badrequest_exception_handler );
115 
116     /**
117      * Returns a map of contentType => list of mime types
118      * \note not available in Python bindings
119      */
120     static const QMap<QgsServerOgcApi::ContentType, QStringList> contentTypeMimes() SIP_SKIP;
121 
122     /**
123      * Returns contentType specializations (e.g. JSON => [GEOJSON, OPENAPI3], XML => [GML])
124      * \note not available in Python bindings
125      */
126     static const QHash<QgsServerOgcApi::ContentType, QList<QgsServerOgcApi::ContentType> > contentTypeAliases() SIP_SKIP;
127 
128     // Utilities
129 #ifndef SIP_RUN
130 
131     /**
132      * Registers an OGC API handler passing \a Args to the constructor
133      * \note not available in Python bindings
134      */
135     template<class T, typename... Args>
registerHandler(Args...args)136     void registerHandler( Args... args )
137     {
138       mHandlers.emplace_back( std::make_shared<T>( args... ) );
139     }
140 #endif
141 
142     /**
143      * Registers an OGC API \a handler, ownership of the handler is transferred to the API
144      */
145     void registerHandler( QgsServerOgcApiHandler *handler SIP_TRANSFER );
146 
147     /**
148      * Returns a sanitized \a url with extra slashes removed and the path URL component that
149      * always starts with a slash.
150      */
151     static QUrl sanitizeUrl( const QUrl &url );
152 
153     /**
154      * Returns the string representation of \a rel attribute.
155      */
156     static std::string relToString( const QgsServerOgcApi::Rel &rel );
157 
158     /**
159      * Returns the string representation of a \a ct (Content-Type) attribute.
160      */
161     static QString contentTypeToString( const QgsServerOgcApi::ContentType &ct );
162 
163     /**
164      * Returns the string representation of a \a ct (Content-Type) attribute.
165      */
166     static std::string contentTypeToStdString( const QgsServerOgcApi::ContentType &ct );
167 
168     /**
169      * Returns the file extension for a \a ct (Content-Type).
170      */
171     static QString contentTypeToExtension( const QgsServerOgcApi::ContentType &ct );
172 
173     /**
174      * Returns the Content-Type value corresponding to \a extension.
175      */
176     static QgsServerOgcApi::ContentType contenTypeFromExtension( const std::string &extension );
177 
178     /**
179      * Returns the mime-type for the \a contentType or an empty string if not found
180      */
181     static std::string mimeType( const QgsServerOgcApi::ContentType &contentType );
182 
183     /**
184      * Returns registered handlers
185      */
186     const std::vector<std::shared_ptr<QgsServerOgcApiHandler> > handlers() const SIP_SKIP;
187 
188   private:
189 
190     QString mRootPath;
191     QString mName;
192     QString mDescription;
193     QString mVersion;
194 
195     //Note: this cannot be unique because of SIP bindings
196     std::vector<std::shared_ptr<QgsServerOgcApiHandler>> mHandlers;
197 
198     //! For each content type, stores a list of at least one content type mime strings aliases
199     static QMap<QgsServerOgcApi::ContentType, QStringList> sContentTypeMime;
200 
201     /**
202      * Stores content type generalization aliases (e.g. JSON->[GEOJSON,OPENAPI3], XML->[GML] )
203      * We are good citizen and we accept JSON from the client (for example) if the actual type
204      * is a JSON-based format (OPENAPI3 or GEOJSON).
205      */
206     static QHash<QgsServerOgcApi::ContentType, QList<QgsServerOgcApi::ContentType>> sContentTypeAliases;
207 
208 };
209 
210 #endif // QGSSERVEROGCAPI_H
211