def test_requestHandler(self): """Test request handler""" headers = { 'header-key-1': 'header-value-1', 'header-key-2': 'header-value-2' } request = QgsBufferServerRequest('http://somesite.com/somepath', QgsServerRequest.GetMethod, headers) response = QgsBufferServerResponse() self.server.handleRequest(request, response) self.assertEqual( bytes(response.body()), b'<ServerException>Project file error</ServerException>\n') self.assertEqual(response.headers(), { 'Content-Length': '54', 'Content-Type': 'text/xml; charset=utf-8' }) self.assertEqual(response.statusCode(), 500)
def get(self, query: str, project: str = None, headers: Dict[str, str] = {}) -> OWSResponse: """ Return server response from query """ request = QgsBufferServerRequest(query, QgsServerRequest.GetMethod, headers, None) response = QgsBufferServerResponse() if project is not None and not os.path.isabs(project): projectpath = self.datapath.join(project) qgsproject = QgsProject() if not qgsproject.read(projectpath.strpath): raise ValueError("Error reading project '%s':" % projectpath.strpath) else: qgsproject = None self.server.handleRequest(request, response, project=qgsproject) return OWSResponse(response)
def _check_white(self, qs): """Check that output is a white image""" req = QgsBufferServerRequest('http://my_server/' + qs) res = QgsBufferServerResponse() self._server.handleRequest(req, res, self.test_project) self.assertEqual(res.statusCode(), 200) result_path = os.path.join(self.temp_dir.path(), 'white.png') with open(result_path, 'wb+') as f: f.write(res.body()) # A full white image is expected image = QImage(result_path) self.assertTrue(image.isGrayscale()) color = image.pixelColor(100, 100) self.assertEqual(color.red(), 255) self.assertEqual(color.green(), 255) self.assertEqual(color.blue(), 255)
def test_requestHandlerProject(self): """Test request handler with none project""" headers = { 'header-key-1': 'header-value-1', 'header-key-2': 'header-value-2' } request = QgsBufferServerRequest('http://somesite.com/somepath', QgsServerRequest.GetMethod, headers) response = QgsBufferServerResponse() self.server.handleRequest(request, response, None) self.assertEqual( bytes(response.body()), b'<ServerException>Project file error. For OWS services: please provide a SERVICE and a MAP parameter pointing to a valid QGIS project file</ServerException>\n' ) self.assertEqual(response.headers(), { 'Content-Length': '156', 'Content-Type': 'text/xml; charset=utf-8' }) self.assertEqual(response.statusCode(), 500)
def do_GET(self, post_body=None): # CGI vars: headers = {} for k, v in self.headers.items(): headers['HTTP_%s' % k.replace(' ', '-').replace('-', '_').replace(' ', '-').upper()] = v request = QgsBufferServerRequest(self.path, (QgsServerRequest.PostMethod if post_body is not None else QgsServerRequest.GetMethod), headers, post_body) response = QgsBufferServerResponse() qgs_server.handleRequest(request, response) headers_dict = response.headers() try: self.send_response(int(headers_dict['Status'].split(' ')[0])) except: self.send_response(200) for k, v in headers_dict.items(): self.send_header(k, v) self.end_headers() self.wfile.write(response.body()) return
def test_landing_page_redirects(self): """Test landing page redirects""" request = QgsBufferServerRequest('http://server.qgis.org/') request.setHeader('Accept', 'application/json') response = QgsBufferServerResponse() self.server.handleRequest(request, response) self.assertEqual(response.headers()[ 'Location'], 'http://server.qgis.org/index.json') response = QgsBufferServerResponse() request.setHeader('Accept', 'text/html') self.server.handleRequest(request, response) self.assertEqual(response.headers()[ 'Location'], 'http://server.qgis.org/index.html')
def testMultiplePks(self): """Test issue GH #41786""" vl = QgsVectorLayer(self.vlconn_multiplepks, 'someData', 'postgres') self.assertTrue(vl.isValid()) p = QgsProject() p.addMapLayers([vl]) json_features_url = ( 'http://qgis/?SERVICE=WMS&REQUEST=GetFeatureInfo&' + 'LAYERS=someData&STYLES=&' + 'INFO_FORMAT=application/json&' + 'SRS=EPSG%3A4326&' + 'QUERY_LAYERS=someData&X=-1&Y=-1&' + 'FEATURE_COUNT=100&') req = QgsBufferServerRequest(json_features_url) res = QgsBufferServerResponse() self.server.handleRequest(req, res, p) j = json.loads(bytes(res.body()).decode('utf8')) self.assertEqual( j, { 'features': [{ 'geometry': None, 'id': 'someData.1@@1', 'properties': { 'name': '1-1', 'pk1': 1, 'pk2': 1 }, 'type': 'Feature' }, { 'geometry': None, 'id': 'someData.1@@2', 'properties': { 'name': '1-2', 'pk1': 1, 'pk2': 2 }, 'type': 'Feature' }], 'type': 'FeatureCollection' })
def ows_server(request): register_django_provider() query_string = request.build_absolute_uri() project_path = os.path.join(os.path.dirname(__file__), 'test.qgs') qgis_project = QgsProject() qgis_project.read(project_path) qgis_request = QgsBufferServerRequest(query_string) qgis_response = QgsBufferServerResponse() server = QgsServer() server.handleRequest(qgis_request, qgis_response, qgis_project) body = bytes(qgis_response.body()) response = HttpResponse(body) for k, v in qgis_response.headers().items(): response[k] = v return response
def test_landing_page_json(self): """Test landing page in JSON format""" request = QgsBufferServerRequest('http://server.qgis.org/index.json') response = QgsBufferServerResponse() self.server.handleRequest(request, response) j_actual = 'Content-Type: application/json\n\n' j_actual += bytes(response.body()).decode('utf8)') if not os.environ.get('TRAVIS', False): expected_path = os.path.join( unitTestDataPath('qgis_server'), 'landingpage', 'test_landing_page_with_pg_index.json') else: expected_path = os.path.join(unitTestDataPath('qgis_server'), 'landingpage', 'test_landing_page_index.json') j_expected = open(expected_path).read() self.compareProjects(j_actual, j_expected, expected_path)
def request(self, method, query: str, project: str = None, data: bytes = None, headers: dict = {}) -> OWSResponse: """ Return server response from query """ request = QgsBufferServerRequest(query, method, headers, data) response = QgsBufferServerResponse() if project is not None and not os.path.isabs(project): projectpath = self.datapath.join(project) qgsproject = QgsProject() if not qgsproject.read(projectpath.strpath): raise ValueError( f"Error reading project '{projectpath.strpath}'") else: qgsproject = None self.server.handleRequest(request, response, project=qgsproject) return OWSResponse(response)
def do_GET(self, post_body=None): # CGI vars: headers = {} for k, v in self.headers.items(): headers['HTTP_%s' % k.replace(' ', '-').replace('-', '_').replace(' ', '-').upper()] = v if not self.path.startswith('http'): self.path = "%s://%s:%s%s" % ('https' if https else 'http', QGIS_SERVER_HOST, self.server.server_port, self.path) request = QgsBufferServerRequest(self.path, (QgsServerRequest.PostMethod if post_body is not None else QgsServerRequest.GetMethod), headers, post_body) response = QgsBufferServerResponse() qgs_server.handleRequest(request, response) headers_dict = response.headers() try: self.send_response(int(headers_dict['Status'].split(' ')[0])) except: self.send_response(200) for k, v in headers_dict.items(): self.send_header(k, v) self.end_headers() self.wfile.write(response.body()) return
def test_project_json(self): """Test landing page project call in JSON format""" # Get hashes for test projects request = QgsBufferServerRequest('http://server.qgis.org/index.json') request.setHeader('Accept', 'application/json') response = QgsBufferServerResponse() self.server.handleRequest(request, response) j = json.loads(bytes(response.body())) test_projects = { p['id']: p['title'].replace(' ', '_') for p in j['projects'] } for identifier, name in test_projects.items(): request = QgsBufferServerRequest('http://server.qgis.org/map/' + identifier) request.setHeader('Accept', 'application/json') self.compareApi(request, None, 'test_project_{}.json'.format( name.replace('.', '_')), subdir='landingpage')
def testOgcApiHandlerContentType(self): """Test OGC API Handler content types""" project = QgsProject() project.read(unitTestDataPath('qgis_server') + '/test_project_api.qgs') request = QgsBufferServerRequest('http://server.qgis.org/api3/handlerthree?value1=9.5') response = QgsBufferServerResponse() # Add handler to API and test for /api3 ctx = QgsServerApiContext('/services/api3', request, response, project, self.server.serverInterface()) api = QgsServerOgcApi(self.server.serverInterface(), '/api3', 'apithree', 'a third api', '1.2') h3 = Handler3() api.registerHandler(h3) ctx = QgsServerApiContext('/services/api3/', request, response, project, self.server.serverInterface()) api.executeRequest(ctx) self.assertEqual(json.loads(bytes(ctx.response().data()))['value1'], 9.5) # Call HTML ctx.request().setUrl(QtCore.QUrl('http://server.qgis.org/api3/handlerthree.html?value1=9.5')) with self.assertRaises(QgsServerApiBadRequestException) as ex: api.executeRequest(ctx) self.assertEqual(str(ex.exception), 'Unsupported Content-Type: HTML') h3.setContentTypes([QgsServerOgcApi.HTML]) with self.assertRaises(QgsServerApiBadRequestException) as ex: api.executeRequest(ctx) self.assertEqual(str(ex.exception), 'Template not found: handlerThree.html') # Define a template path dir = QtCore.QTemporaryDir() with open(dir.path() + '/handlerThree.html', 'w+') as f: f.write("Hello world") h3.templatePathOverride = dir.path() + '/handlerThree.html' ctx.response().clear() api.executeRequest(ctx) self.assertEqual(bytes(ctx.response().data()), b"Hello world")
def baseDoRequest(self, q): request = self.request # Uppercase REQUEST if 'REQUEST' in [k.upper() for k in q.keys()]: if request.method == 'GET': ows_request = q['REQUEST'].upper() else: if request.content_type == 'application/x-www-form-urlencoded': ows_request = request.POST['REQUEST'].upper() else: ows_request = request.POST['REQUEST'][0].upper() q['REQUEST'] = ows_request # FIXME: proxy or redirect in case of WMS/WFS/XYZ cascading? qgs_project = get_qgs_project(self.project.qgis_file.path) if qgs_project is None: raise Http404('The requested QGIS project could not be loaded!') data = None if request.method == 'GET': method = QgsBufferServerRequest.GetMethod elif request.method == 'POST': method = QgsBufferServerRequest.PostMethod data = request.body elif request.method == 'PUT': method = QgsBufferServerRequest.PutMethod data = request.body elif request.method == 'PATCH': method = QgsBufferServerRequest.PatchMethod data = request.body elif request.method == 'HEAD': method = QgsBufferServerRequest.HeadMethod elif request.method == 'DELETE': method = QgsBufferServerRequest.DeleteMethod else: logger.warning("Request method not supported: %s, assuming GET" % request.method) method = QgsBufferServerRequest.GetMethod headers = {} for header_key in request.headers.keys(): headers[header_key] = request.headers.get(header_key) uri = request.build_absolute_uri(request.path) + '?' + q.urlencode() logger.debug('Calling QGIS Server: %s' % uri) qgs_request = QgsBufferServerRequest(uri, method, headers, data) # Attach user and project to the server object to make them accessible by the # server access control plugins (constraints etc.) QGS_SERVER.user = request.user QGS_SERVER.project = self.project qgs_response = QgsBufferServerResponse() try: QGS_SERVER.handleRequest(qgs_request, qgs_response, qgs_project) except Exception as ex: return HttpResponseServerError( reason="Error handling server request: %s" % ex) response = HttpResponse(bytes(qgs_response.body())) response.status_code = qgs_response.statusCode() for key, value in qgs_response.headers().items(): response[key] = value return response
def test_getcapabilities(self): project = self._project_path assert os.path.exists(project), "Project file not found: " + project # without cache query_string = '?MAP=%s&SERVICE=WMS&VERSION=1.3.0&REQUEST=%s' % ( urllib.parse.quote(project), 'GetCapabilities') header, body = self._execute_request(query_string) doc = QDomDocument("wms_getcapabilities_130.xml") doc.setContent(body) # with cache header, body = self._execute_request(query_string) # without cache query_string = '?MAP=%s&SERVICE=WMS&VERSION=1.1.1&REQUEST=%s' % ( urllib.parse.quote(project), 'GetCapabilities') header, body = self._execute_request(query_string) # with cache header, body = self._execute_request(query_string) # without cache query_string = '?MAP=%s&SERVICE=WFS&VERSION=1.1.0&REQUEST=%s' % ( urllib.parse.quote(project), 'GetCapabilities') header, body = self._execute_request(query_string) # with cache header, body = self._execute_request(query_string) # without cache query_string = '?MAP=%s&SERVICE=WFS&VERSION=1.0.0&REQUEST=%s' % ( urllib.parse.quote(project), 'GetCapabilities') header, body = self._execute_request(query_string) # with cache header, body = self._execute_request(query_string) # without cache query_string = '?MAP=%s&SERVICE=WCS&VERSION=1.0.0&REQUEST=%s' % ( urllib.parse.quote(project), 'GetCapabilities') header, body = self._execute_request(query_string) # with cache header, body = self._execute_request(query_string) # without cache query_string = '?MAP=%s&SERVICE=WMTS&VERSION=1.0.0&REQUEST=%s' % ( urllib.parse.quote(project), 'GetCapabilities') header, body = self._execute_request(query_string) # with cache header, body = self._execute_request(query_string) filelist = [ f for f in os.listdir(self._servercache._cache_dir) if f.endswith(".xml") ] self.assertEqual(len(filelist), 6, 'Not enough file in cache') cacheManager = self._server_iface.cacheManager() self.assertTrue(cacheManager.deleteCachedDocuments(None), 'deleteCachedDocuments does not return True') filelist = [ f for f in os.listdir(self._servercache._cache_dir) if f.endswith(".xml") ] self.assertEqual(len(filelist), 0, 'All files in cache are not deleted ') prj = QgsProject() prj.read(project) query_string = '?MAP=%s&SERVICE=WMS&VERSION=1.3.0&REQUEST=%s' % ( urllib.parse.quote(project), 'GetCapabilities') request = QgsBufferServerRequest(query_string, QgsServerRequest.GetMethod, {}, None) accessControls = self._server_iface.accessControls() cDoc = QDomDocument("wms_getcapabilities_130.xml") self.assertFalse( cacheManager.getCachedDocument(cDoc, prj, request, accessControls), 'getCachedDocument is not None') self.assertTrue( cacheManager.setCachedDocument(doc, prj, request, accessControls), 'setCachedDocument false') self.assertTrue( cacheManager.getCachedDocument(cDoc, prj, request, accessControls), 'getCachedDocument is None') self.assertEqual(doc.documentElement().tagName(), cDoc.documentElement().tagName(), 'cachedDocument not equal to provide document') self.assertTrue(cacheManager.deleteCachedDocuments(None), 'deleteCachedDocuments does not return True')
def test_wms_getprint_maptheme_highlight(self): """Test templates green and red have 2 layers: red and green template red: follow map theme red template green: follow map theme green template blank: no map theme """ project = self.project params = { 'SERVICE': 'WMS', 'VERSION': '1.3.0', 'REQUEST': 'GetPrint', 'TEMPLATE': 'blank', 'FORMAT': 'png', 'LAYERS': '', 'map0:EXTENT': '44.92867722467413216,7.097696894150993252,45.0714498943264914,7.378188333645007368', 'map0:LAYERS': 'red', 'CRS': 'EPSG:4326', 'DPI': '72', 'map0:HIGHLIGHT_GEOM': self.polygon, 'map0:HIGHLIGHT_SYMBOL': r'<StyledLayerDescriptor><UserStyle><FeatureTypeStyle><Rule><PolygonSymbolizer><Fill><CssParameter name="fill">%230000FF</CssParameter></Fill></PolygonSymbolizer></Rule></FeatureTypeStyle></UserStyle></StyledLayerDescriptor>' } response = QgsBufferServerResponse() request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") self._assertBlue(image.pixelColor(100, 100)) # Test highlight without layers params["TEMPLATE"] = "blank" params["map0:LAYERS"] = "" response = QgsBufferServerResponse() request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") self._assertBlue(image.pixelColor(100, 100)) # Test highlight on follow theme (issue GH #34178) params["TEMPLATE"] = "red" response = QgsBufferServerResponse() request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") self._assertBlue(image.pixelColor(100, 100)) # Test highlight on follow theme (issue GH #34178) params["TEMPLATE"] = "green" response = QgsBufferServerResponse() request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") self._assertBlue(image.pixelColor(100, 100)) # Test highlight on follow theme, but add LAYERS (issue GH #34178) params["TEMPLATE"] = "green" params["LAYERS"] = "red" response = QgsBufferServerResponse() request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") self._assertBlue(image.pixelColor(100, 100))
def test_wfs3_field_filters_star(self): """Test field filters""" project = QgsProject() project.read(unitTestDataPath('qgis_server') + '/test_project.qgs') request = QgsBufferServerRequest('http://server.qgis.org/wfs3/collections/testlayer3/items?name=tw*') self.compareApi(request, project, 'test_wfs3_collections_items_testlayer3_name_eq_tw_star.json')
def testOgcApiHandler(self): """Test OGC API Handler""" project = QgsProject() project.read(unitTestDataPath('qgis_server') + '/test_project.qgs') request = QgsBufferServerRequest( 'http://server.qgis.org/wfs3/collections/testlayer%20èé/items?limit=-1' ) response = QgsBufferServerResponse() ctx = QgsServerApiContext('/services/api1', request, response, project, self.server.serverInterface()) h = Handler1() self.assertTrue( h.staticPath(ctx).endswith('/resources/server/api/ogc/static')) self.assertEqual(h.path(), QtCore.QRegularExpression("/handlerone")) self.assertEqual(h.description(), 'The first handler ever') self.assertEqual(h.operationId(), 'handlerOne') self.assertEqual(h.summary(), 'First of its name') self.assertEqual(h.linkTitle(), 'Handler One Link Title') self.assertEqual(h.linkType(), QgsServerOgcApi.data) with self.assertRaises(QgsServerApiBadRequestException) as ex: h.handleRequest(ctx) self.assertEqual(str(ex.exception), 'Missing required argument: \'value1\'') r = ctx.response() self.assertEqual(r.data(), '') with self.assertRaises(QgsServerApiBadRequestException) as ex: h.values(ctx) self.assertEqual(str(ex.exception), 'Missing required argument: \'value1\'') # Add handler to API and test for /api2 ctx = QgsServerApiContext('/services/api2', request, response, project, self.server.serverInterface()) api = QgsServerOgcApi(self.server.serverInterface(), '/api2', 'apitwo', 'a second api', '1.2') api.registerHandler(h) # Add a second handler (will be tested later) h2 = Handler2() api.registerHandler(h2) ctx.request().setUrl(QtCore.QUrl('http://www.qgis.org/services/api1')) with self.assertRaises(QgsServerApiBadRequestException) as ex: api.executeRequest(ctx) self.assertEqual( str(ex.exception), 'Requested URI does not match any registered API handler') ctx.request().setUrl(QtCore.QUrl('http://www.qgis.org/services/api2')) with self.assertRaises(QgsServerApiBadRequestException) as ex: api.executeRequest(ctx) self.assertEqual( str(ex.exception), 'Requested URI does not match any registered API handler') ctx.request().setUrl( QtCore.QUrl('http://www.qgis.org/services/api2/handlerone')) with self.assertRaises(QgsServerApiBadRequestException) as ex: api.executeRequest(ctx) self.assertEqual(str(ex.exception), 'Missing required argument: \'value1\'') ctx.request().setUrl( QtCore.QUrl( 'http://www.qgis.org/services/api2/handlerone?value1=not+a+double' )) with self.assertRaises(QgsServerApiBadRequestException) as ex: api.executeRequest(ctx) self.assertEqual( str(ex.exception), 'Argument \'value1\' could not be converted to Double') ctx.request().setUrl( QtCore.QUrl( 'http://www.qgis.org/services/api2/handlerone?value1=1.2345')) params = h.values(ctx) self.assertEqual(params, {'value1': 1.2345}) api.executeRequest(ctx) self.assertEqual( json.loads(bytes(ctx.response().data()))['value1'], 1.2345) # Test path fragments extraction ctx.request().setUrl( QtCore.QUrl( 'http://www.qgis.org/services/api2/handlertwo/00/555?value1=1.2345' )) params = h2.values(ctx) self.assertEqual(params, { 'code1': '00', 'value1': 1.2345, 'value2': None }) # Test string encoding ctx.request().setUrl( QtCore.QUrl( 'http://www.qgis.org/services/api2/handlertwo/00/555?value1=1.2345&value2=a%2Fstring%20some' )) params = h2.values(ctx) self.assertEqual(params, { 'code1': '00', 'value1': 1.2345, 'value2': 'a/string some' }) # Test links self.assertEqual( h2.href(ctx), 'http://www.qgis.org/services/api2/handlertwo/00/555?value1=1.2345&value2=a%2Fstring%20some' ) self.assertEqual( h2.href(ctx, '/extra'), 'http://www.qgis.org/services/api2/handlertwo/00/555/extra?value1=1.2345&value2=a%2Fstring%20some' ) self.assertEqual( h2.href(ctx, '/extra', 'json'), 'http://www.qgis.org/services/api2/handlertwo/00/555/extra.json?value1=1.2345&value2=a%2Fstring%20some' ) # Test template path self.assertTrue( h2.templatePath(ctx).endswith( '/resources/server/api/ogc/templates/services/api2/handlerTwo.html' ))
def _assert_status_code(self, status_code, qs, requestMethod=QgsServerRequest.GetMethod, data=None, project=None): request = QgsBufferServerRequest(qs, requestMethod, {}, data) response = QgsBufferServerResponse() self.server.handleRequest(request, response, project) assert response.statusCode() == status_code, "%s != %s" % (response.statusCode(), status_code)
def test_wfs3_landing_page(self): """Test WFS3 API landing page in HTML format""" request = QgsBufferServerRequest('http://server.qgis.org/wfs3.html') self.compareApi(request, None, 'test_wfs3_landing_page.html')
def test_wfs3_conformance(self): """Test WFS3 API""" request = QgsBufferServerRequest( 'http://server.qgis.org/wfs3/conformance') self.compareApi(request, None, 'test_wfs3_conformance.json')
def test_wms_getprint_maptheme_multiple_maps(self): """Test template points has 4 layers: points_black, points_red, points_green, points_blue the template has two maps (from top to bottom) map1 and map0 using respectively the 4points-red and 4points-green map themes """ project = self.project # No LAYERS specified params = { 'SERVICE': 'WMS', 'VERSION': '1.3.0', 'REQUEST': 'GetPrint', 'TEMPLATE': 'points', 'FORMAT': 'png', 'map0:EXTENT': '44.66151222233335716,6.71202136069002187,45.25042454764368927,7.83398711866607833', 'CRS': 'EPSG:4326', 'DPI': '72', 'map1:EXTENT': '44.66151222233335716,6.71202136069002187,45.25042454764368927,7.83398711866607833' } response = QgsBufferServerResponse() request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") # Expected: green and red # map1 (top map) self._assertRed(image.pixelColor(325, 184)) # RED self._assertWhite(image.pixelColor(474, 184)) # GREEN self._assertWhite(image.pixelColor(332, 262)) # BLUE self._assertWhite(image.pixelColor(485, 258)) # BLACK # map0 (bottom map) self._assertWhite(image.pixelColor(315, 461)) # RED self._assertGreen(image.pixelColor(475, 473)) # GREEN self._assertWhite(image.pixelColor(329, 553)) # BLUE self._assertWhite(image.pixelColor(481, 553)) # BLACK # Black LAYERS params["LAYERS"] = "points_black" response = QgsBufferServerResponse() request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") # Expected black # map1 (top map) self._assertWhite(image.pixelColor(325, 184)) # RED self._assertWhite(image.pixelColor(474, 184)) # GREEN self._assertWhite(image.pixelColor(332, 262)) # BLUE self._assertBlack(image.pixelColor(485, 258)) # BLACK # map0 (bottom map) self._assertWhite(image.pixelColor(315, 461)) # RED self._assertWhite(image.pixelColor(475, 473)) # GREEN self._assertWhite(image.pixelColor(329, 553)) # BLUE self._assertBlack(image.pixelColor(481, 553)) # BLACK # Black map0:LAYERS del params["LAYERS"] params["map0:LAYERS"] = "points_black" response = QgsBufferServerResponse() request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") # Expected black on map0, green on map1 # map1 (top map) self._assertRed(image.pixelColor(325, 184)) # RED self._assertWhite(image.pixelColor(474, 184)) # GREEN self._assertWhite(image.pixelColor(332, 262)) # BLUE self._assertWhite(image.pixelColor(485, 258)) # BLACK # map0 (bottom map) self._assertWhite(image.pixelColor(315, 461)) # RED self._assertWhite(image.pixelColor(475, 473)) # GREEN self._assertWhite(image.pixelColor(329, 553)) # BLUE self._assertBlack(image.pixelColor(481, 553)) # BLACK # Conflicting information: Black LAYERS and Green map0:LAYERS # The second gets precedence on map0 while LAYERS is applied to map1 params["map0:LAYERS"] = "points_blue" params["LAYERS"] = "points_black" response = QgsBufferServerResponse() request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") # Expected green on map0, black on map1 # map1 (top map) self._assertWhite(image.pixelColor(325, 184)) # RED self._assertWhite(image.pixelColor(474, 184)) # GREEN self._assertWhite(image.pixelColor(332, 262)) # BLUE self._assertBlack(image.pixelColor(485, 258)) # BLACK # map0 (bottom map) self._assertWhite(image.pixelColor(315, 461)) # RED self._assertWhite(image.pixelColor(475, 473)) # GREEN self._assertBlue(image.pixelColor(329, 553)) # BLUE self._assertWhite(image.pixelColor(481, 553)) # BLACK
def _test_error(uri): request = QgsBufferServerRequest(uri) request.setHeader('Accept', 'application/json') response = QgsBufferServerResponse() self.server.handleRequest(request, response) self.assertEqual(bytes(response.body()), b'<ServerException>Project file error. For OWS services: please provide a SERVICE and a MAP parameter pointing to a valid QGIS project file</ServerException>\n')
def test_landing_page(self): request = QgsBufferServerRequest('/') response = QgsBufferServerResponse() self.server.handleRequest(request, response) self.assertTrue(b'html' in bytes(response.body()), response.body()) self.assertEqual(response.statusCode(), 200)
def test_wms_getprint_maptheme(self): """Test templates green and red have 2 layers: red and green template red: follow map theme red template green: follow map theme green template blank: no map theme """ project = self.project params = { "SERVICE": "WMS", "VERSION": "1.3.0", "REQUEST": "GetPrint", "TEMPLATE": "blank", "FORMAT": "png", "LAYERS": "", "map0:EXTENT": "44.92867722467413216,7.097696894150993252,45.0714498943264914,7.378188333645007368", "map0:LAYERS": "red", "CRS": "EPSG:4326", "DPI": '72' } ###################################################### # Template map theme tests, no HIGHLIGHT # blank template, specified layer is red response = QgsBufferServerResponse() request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") self._assertRed(image.pixelColor(100, 100)) # blank template, specified layer is green params["map0:LAYERS"] = "green" response = QgsBufferServerResponse() request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") self._assertGreen(image.pixelColor(100, 100)) # red template, no specified layers params["map0:LAYERS"] = "" params["TEMPLATE"] = "red" response = QgsBufferServerResponse() request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") self._assertRed(image.pixelColor(100, 100)) # green template, no specified layers params["map0:LAYERS"] = "" params["TEMPLATE"] = "green" response = QgsBufferServerResponse() request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") self._assertGreen(image.pixelColor(100, 100)) # green template, specified layer is red # This is a conflict situation: the green template map is set to follow green theme # but we tell the server to render the red layer, red is what we get. params["map0:LAYERS"] = "red" params["TEMPLATE"] = "green" response = QgsBufferServerResponse() request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") self._assertRed(image.pixelColor(100, 100)) # Same situation as above but LAYERS is not map0 prefixed params["LAYERS"] = "red" params["TEMPLATE"] = "green" response = QgsBufferServerResponse() request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") self._assertRed(image.pixelColor(100, 100)) # Same as above but we have a conflict situation: we pass both LAYERS # and map0:LAYERS, the second must prevail because it is more specific params["LAYERS"] = "red" params["map0:LAYERS"] = "green" params["TEMPLATE"] = "red" response = QgsBufferServerResponse() request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") self._assertGreen(image.pixelColor(100, 100))
def baseDoRequest(self, q): request = self.request # Uppercase REQUEST if 'REQUEST' in [k.upper() for k in q.keys()]: if request.method == 'GET': ows_request = q['REQUEST'].upper() else: if request.content_type == 'application/x-www-form-urlencoded': ows_request = request.POST['REQUEST'].upper() else: ows_request = request.POST['REQUEST'][0].upper() q['REQUEST'] = ows_request # FIXME: proxy or redirect in case of WMS/WFS/XYZ cascading? qgs_project = get_qgs_project(self.project.qgis_file.path) if qgs_project is None: raise Http404('The requested QGIS project could not be loaded!') data = None if request.method == 'GET': method = QgsBufferServerRequest.GetMethod elif request.method == 'POST': method = QgsBufferServerRequest.PostMethod data = request.body elif request.method == 'PUT': method = QgsBufferServerRequest.PutMethod data = request.body elif request.method == 'PATCH': method = QgsBufferServerRequest.PatchMethod data = request.body elif request.method == 'HEAD': method = QgsBufferServerRequest.HeadMethod elif request.method == 'DELETE': method = QgsBufferServerRequest.DeleteMethod else: logger.warning( "Request method not supported: %s, assuming GET" % request.method) method = QgsBufferServerRequest.GetMethod headers = {} for header_key in request.headers.keys(): headers[header_key] = request.headers.get(header_key) uri = request.build_absolute_uri(request.path) + '?' + q.urlencode() logger.debug('Calling QGIS Server: %s' % uri) qgs_request = QgsBufferServerRequest(uri, method, headers, data) # Attach user and project to the server object to make them accessible by the # server access control plugins (constraints etc.) QGS_SERVER.djrequest = request QGS_SERVER.user = request.user QGS_SERVER.project = self.project # For GetPrint QGIS functions that rely on layers visibility, we need to check # the layers from LAYERS use_ids = QgsServerProjectUtils.wmsUseLayerIds(qgs_project) tree_root = qgs_project.layerTreeRoot() # Loop through the layers and make them visible for layer_name in qgs_request.queryParameter('LAYERS').split(','): layer_name = urllib.parse.unquote(layer_name) layer = None if use_ids: layer = qgs_project.mapLayer(layer_name) else: try: layer = qgs_project.mapLayersByName(layer_name)[0] except: # short name? for l in qgs_project.mapLayers().values(): if l.shortName() == layer_name: layer = l break if layer is None: logger.warning( 'Could not find layer "{}" when configuring OWS call'.format(layer_name)) else: layer_node = tree_root.findLayer(layer) if layer_node is not None: layer_node.setItemVisibilityCheckedParentRecursive(True) else: logger.warning( 'Could not find layer tree node "{}" when configuring OWS call'.format(layer_name)) qgs_response = QgsBufferServerResponse() try: QGS_SERVER.handleRequest(qgs_request, qgs_response, qgs_project) except Exception as ex: return HttpResponseServerError(reason="Error handling server request: %s" % ex) response = HttpResponse(bytes(qgs_response.body())) response.status_code = qgs_response.statusCode() for key, value in qgs_response.headers().items(): response[key] = value return response
def test_wfs3_collection_items_limit(self): """Test WFS3 API item limits""" project = QgsProject() project.read(unitTestDataPath('qgis_server') + '/test_project_api.qgs') request = QgsBufferServerRequest('http://server.qgis.org/wfs3/collections/testlayer%20èé/items?limit=1') self.compareApi(request, project, 'test_wfs3_collections_items_testlayer_èé_limit_1.json')
def test_wfs3_collections_html(self): """Test WFS3 API collections in html format""" request = QgsBufferServerRequest('http://server.qgis.org/wfs3/collections.html') project = QgsProject() project.read(unitTestDataPath('qgis_server') + '/test_project_api.qgs') self.compareApi(request, project, 'test_wfs3_collections_project.html')
def compareContentType(self, url, headers, content_type): request = QgsBufferServerRequest(url, headers=headers) response = QgsBufferServerResponse() self.server.handleRequest(request, response, QgsProject()) self.assertEqual(response.headers()['Content-Type'], content_type)
def test_wms_getprint_maptheme(self): """Test project has 2 layer: red and green and three templates: red: follow map theme red green: follow map theme green blank: no map theme """ tmp_dir = QTemporaryDir() shutil.copyfile( os.path.join(unitTestDataPath('qgis_server'), 'test_project_mapthemes.qgs'), os.path.join(tmp_dir.path(), 'test_project_mapthemes.qgs')) shutil.copyfile( os.path.join(unitTestDataPath('qgis_server'), 'test_project_mapthemes.gpkg'), os.path.join(tmp_dir.path(), 'test_project_mapthemes.gpkg')) project = QgsProject() self.assertTrue( project.read( os.path.join(tmp_dir.path(), 'test_project_mapthemes.qgs'))) params = { "SERVICE": "WMS", "VERSION": "1.3", "REQUEST": "GetPrint", "TEMPLATE": "blank", "FORMAT": "png", "LAYERS": "", "map0:EXTENT": "44.92867722467413216,7.097696894150993252,45.0714498943264914,7.378188333645007368", "map0:LAYERS": "red", "CRS": "EPSG:4326", "DPI": '72' } polygon = 'POLYGON((7.09769689415099325 44.92867722467413216, 7.37818833364500737 44.92867722467413216, 7.37818833364500737 45.0714498943264914, 7.09769689415099325 45.0714498943264914, 7.09769689415099325 44.92867722467413216))' ###################################################### # Template map theme tests, no HIGHLIGHT # blank template, specified layer is red response = QgsBufferServerResponse() request = QgsBufferServerRequest( '?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") self._assertRed(image.pixelColor(100, 100)) # blank template, specified layer is green params["map0:LAYERS"] = "green" response = QgsBufferServerResponse() request = QgsBufferServerRequest( '?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") self._assertGreen(image.pixelColor(100, 100)) # red template, no specified layers params["map0:LAYERS"] = "" params["TEMPLATE"] = "red" response = QgsBufferServerResponse() request = QgsBufferServerRequest( '?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") self._assertRed(image.pixelColor(100, 100)) # green template, no specified layers params["map0:LAYERS"] = "" params["TEMPLATE"] = "green" response = QgsBufferServerResponse() request = QgsBufferServerRequest( '?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") self._assertGreen(image.pixelColor(100, 100)) # green template, specified layer is red # This is a conflict situation: the green template map is set to follow green theme # but we tell the server to render the red layer, red is what we get. params["map0:LAYERS"] = "red" params["TEMPLATE"] = "green" response = QgsBufferServerResponse() request = QgsBufferServerRequest( '?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") self._assertRed(image.pixelColor(100, 100)) ###################################################### # Start HIGHLIGHT tests params["TEMPLATE"] = "blank" params["map0:LAYERS"] = "red" params["map0:HIGHLIGHT_GEOM"] = polygon params[ "map0:HIGHLIGHT_SYMBOL"] = r'<StyledLayerDescriptor><UserStyle><FeatureTypeStyle><Rule><PolygonSymbolizer><Fill><CssParameter name="fill">%230000FF</CssParameter></Fill></PolygonSymbolizer></Rule></FeatureTypeStyle></UserStyle></StyledLayerDescriptor>' response = QgsBufferServerResponse() request = QgsBufferServerRequest( '?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") self._assertBlue(image.pixelColor(100, 100)) # Test highlight without layers params["TEMPLATE"] = "blank" params["map0:LAYERS"] = "" response = QgsBufferServerResponse() request = QgsBufferServerRequest( '?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") self._assertBlue(image.pixelColor(100, 100)) # Test highlight on follow theme (issue GH #34178) params["TEMPLATE"] = "red" response = QgsBufferServerResponse() request = QgsBufferServerRequest( '?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") self._assertBlue(image.pixelColor(100, 100)) # Test highlight on follow theme (issue GH #34178) params["TEMPLATE"] = "green" response = QgsBufferServerResponse() request = QgsBufferServerRequest( '?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") self._assertBlue(image.pixelColor(100, 100)) # Test highlight on follow theme, but add LAYERS (issue GH #34178) params["TEMPLATE"] = "green" params["LAYERS"] = "red" response = QgsBufferServerResponse() request = QgsBufferServerRequest( '?' + '&'.join(["%s=%s" % i for i in params.items()])) self.server.handleRequest(request, response, project) image = QImage.fromData(response.body(), "PNG") self._assertBlue(image.pixelColor(100, 100))