c2cgeoportal-geoportal 2.3.5.79__py3-none-any.whl → 2.9rc44__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- c2cgeoportal_geoportal/__init__.py +960 -0
- c2cgeoportal_geoportal/lib/__init__.py +256 -0
- c2cgeoportal_geoportal/lib/authentication.py +250 -0
- c2cgeoportal_geoportal/lib/bashcolor.py +46 -0
- c2cgeoportal_geoportal/lib/cacheversion.py +77 -0
- c2cgeoportal_geoportal/lib/caching.py +176 -0
- c2cgeoportal_geoportal/lib/check_collector.py +80 -0
- c2cgeoportal_geoportal/lib/checker.py +295 -0
- c2cgeoportal_geoportal/lib/common_headers.py +172 -0
- c2cgeoportal_geoportal/lib/dbreflection.py +266 -0
- c2cgeoportal_geoportal/lib/filter_capabilities.py +360 -0
- c2cgeoportal_geoportal/lib/fulltextsearch.py +50 -0
- c2cgeoportal_geoportal/lib/functionality.py +166 -0
- c2cgeoportal_geoportal/lib/headers.py +62 -0
- c2cgeoportal_geoportal/lib/i18n.py +38 -0
- c2cgeoportal_geoportal/lib/layers.py +132 -0
- c2cgeoportal_geoportal/lib/lingva_extractor.py +937 -0
- c2cgeoportal_geoportal/lib/loader.py +57 -0
- c2cgeoportal_geoportal/lib/metrics.py +117 -0
- c2cgeoportal_geoportal/lib/oauth2.py +1186 -0
- c2cgeoportal_geoportal/lib/oidc.py +304 -0
- c2cgeoportal_geoportal/lib/wmstparsing.py +353 -0
- c2cgeoportal_geoportal/lib/xsd.py +166 -0
- c2cgeoportal_geoportal/py.typed +0 -0
- c2cgeoportal_geoportal/resources.py +49 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/ci/config.yaml +26 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/cookiecutter.json +18 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/.dockerignore +6 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/.eslintrc.yaml +19 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/.prospector.yaml +30 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/Dockerfile +75 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/Makefile +6 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/alembic.ini +58 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/alembic.yaml +19 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/development.ini +121 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/gunicorn.conf.py +139 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/language_mapping +3 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/lingva-client.cfg +5 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/lingva-server.cfg +6 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/production.ini +38 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/requirements.txt +2 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/setup.py +25 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/webpack.api.js +41 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/webpack.apps.js +64 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/webpack.commons.js +11 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/webpack.config.js +22 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/__init__.py +42 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/authentication.py +10 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/dev.py +14 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/models.py +8 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/multi_organization.py +7 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/resources.py +11 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static-ngeo/api/index.js +12 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static-ngeo/js/{{cookiecutter.package}}module.js +25 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/subscribers.py +39 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/views/__init__.py +0 -0
- c2cgeoportal_geoportal/scaffolds/advance_update/cookiecutter.json +18 -0
- c2cgeoportal_geoportal/scaffolds/advance_update/{{cookiecutter.project}}/geoportal/CONST_Makefile +121 -0
- c2cgeoportal_geoportal/scaffolds/create/cookiecutter.json +18 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.dockerignore +14 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.editorconfig +17 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/main.yaml +73 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/rebuild.yaml +50 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/update_l10n.yaml +66 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.gitignore +16 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.pre-commit-config.yaml +35 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.prettierignore +1 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.prettierrc.yaml +2 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Dockerfile +75 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Makefile +70 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/README.rst +29 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/build +179 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/config.yaml +22 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/docker-compose-check +25 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/requirements.txt +2 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-db.yaml +24 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-lib.yaml +513 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-qgis.yaml +21 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.override.sample.yaml +65 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.yaml +121 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/env.default +102 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/env.project +69 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/vars.yaml +430 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/locale/en/LC_MESSAGES/{{cookiecutter.package}}_geoportal-client.po +6 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/desktop.css +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/iframe_api.css +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/mobile.css +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/banner_left.png +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/banner_right.png +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/blank.png +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker-blue.png +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker-gold.png +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker-green.png +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker.png +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/robot.txt.tmpl +3 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/data/Readme.txt +69 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/data/TM_EUROPE_BORDERS-0.3.sql +70 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/demo.map.tmpl +224 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/Arial.ttf +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/Arialbd.ttf +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/Arialbi.ttf +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/Ariali.ttf +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/NotoSans-Bold.ttf +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/NotoSans-BoldItalic.ttf +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/NotoSans-Italic.ttf +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/NotoSans-Regular.ttf +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/Verdana.ttf +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/Verdanab.ttf +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/Verdanai.ttf +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/Verdanaz.ttf +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts.conf +12 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/mapserver.conf +16 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/mapserver.map.tmpl +87 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/tinyows.xml.tmpl +36 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/A3_Landscape.jrxml +207 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/A3_Portrait.jrxml +185 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/A4_Landscape.jrxml +200 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/A4_Portrait.jrxml +170 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/config.yaml.tmpl +175 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/legend.jrxml +109 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/localisation.properties +4 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/localisation_fr.properties +4 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/logo.png +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/north.svg +93 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/results.jrxml +25 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/project.yaml +18 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/pyproject.toml +7 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/qgisserver/pg_service.conf.tmpl +15 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/run_alembic.sh +11 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-backup +126 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-restore +132 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/setup.cfg +7 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/spell-ignore-words.txt +5 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tests/__init__.py +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tests/test_app.py +78 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tilegeneration/config.yaml.tmpl +195 -0
- c2cgeoportal_geoportal/scaffolds/update/cookiecutter.json +18 -0
- c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/.upgrade.yaml +67 -0
- c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_CHANGELOG.txt +304 -0
- c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_create_template/tests/test_testapp.py +48 -0
- c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/geoportal/.CONST_vars.yaml.swp +0 -0
- c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/geoportal/CONST_config-schema.yaml +927 -0
- c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/geoportal/CONST_vars.yaml +1503 -0
- c2cgeoportal_geoportal/scripts/__init__.py +64 -0
- c2cgeoportal_geoportal/scripts/c2cupgrade.py +879 -0
- c2cgeoportal_geoportal/scripts/create_demo_theme.py +83 -0
- c2cgeoportal_geoportal/scripts/manage_users.py +140 -0
- c2cgeoportal_geoportal/scripts/pcreate.py +296 -0
- c2cgeoportal_geoportal/scripts/theme2fts.py +347 -0
- c2cgeoportal_geoportal/scripts/urllogin.py +81 -0
- c2cgeoportal_geoportal/templates/login.html +90 -0
- c2cgeoportal_geoportal/templates/notlogin.html +62 -0
- c2cgeoportal_geoportal/templates/testi18n.html +12 -0
- c2cgeoportal_geoportal/views/__init__.py +59 -0
- c2cgeoportal_geoportal/views/dev.py +57 -0
- c2cgeoportal_geoportal/views/dynamic.py +208 -0
- c2cgeoportal_geoportal/views/entry.py +174 -0
- c2cgeoportal_geoportal/views/fulltextsearch.py +189 -0
- c2cgeoportal_geoportal/views/geometry_processing.py +75 -0
- c2cgeoportal_geoportal/views/i18n.py +129 -0
- c2cgeoportal_geoportal/views/layers.py +713 -0
- c2cgeoportal_geoportal/views/login.py +684 -0
- c2cgeoportal_geoportal/views/mapserverproxy.py +234 -0
- c2cgeoportal_geoportal/views/memory.py +90 -0
- c2cgeoportal_geoportal/views/ogcproxy.py +120 -0
- c2cgeoportal_geoportal/views/pdfreport.py +245 -0
- c2cgeoportal_geoportal/views/printproxy.py +143 -0
- c2cgeoportal_geoportal/views/profile.py +192 -0
- c2cgeoportal_geoportal/views/proxy.py +261 -0
- c2cgeoportal_geoportal/views/raster.py +233 -0
- c2cgeoportal_geoportal/views/resourceproxy.py +73 -0
- c2cgeoportal_geoportal/views/shortener.py +152 -0
- c2cgeoportal_geoportal/views/theme.py +1322 -0
- c2cgeoportal_geoportal/views/tinyowsproxy.py +189 -0
- c2cgeoportal_geoportal/views/vector_tiles.py +83 -0
- {c2cgeoportal_geoportal-2.3.5.79.dist-info → c2cgeoportal_geoportal-2.9rc44.dist-info}/METADATA +21 -24
- c2cgeoportal_geoportal-2.9rc44.dist-info/RECORD +193 -0
- {c2cgeoportal_geoportal-2.3.5.79.dist-info → c2cgeoportal_geoportal-2.9rc44.dist-info}/WHEEL +1 -1
- c2cgeoportal_geoportal-2.9rc44.dist-info/entry_points.txt +28 -0
- c2cgeoportal_geoportal-2.9rc44.dist-info/top_level.txt +2 -0
- tests/__init__.py +100 -0
- tests/test_cachebuster.py +71 -0
- tests/test_caching.py +275 -0
- tests/test_checker.py +85 -0
- tests/test_decimaljson.py +47 -0
- tests/test_headerstween.py +64 -0
- tests/test_i18n.py +31 -0
- tests/test_init.py +193 -0
- tests/test_locale_negociator.py +69 -0
- tests/test_mapserverproxy_route_predicate.py +64 -0
- tests/test_raster.py +267 -0
- tests/test_wmstparsing.py +238 -0
- tests/xmlstr.py +103 -0
- c2cgeoportal_geoportal-2.3.5.79.dist-info/DESCRIPTION.rst +0 -8
- c2cgeoportal_geoportal-2.3.5.79.dist-info/RECORD +0 -7
- c2cgeoportal_geoportal-2.3.5.79.dist-info/entry_points.txt +0 -22
- c2cgeoportal_geoportal-2.3.5.79.dist-info/metadata.json +0 -1
- c2cgeoportal_geoportal-2.3.5.79.dist-info/top_level.txt +0 -1
@@ -0,0 +1,143 @@
|
|
1
|
+
# Copyright (c) 2011-2024, Camptocamp SA
|
2
|
+
# All rights reserved.
|
3
|
+
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
# list of conditions and the following disclaimer.
|
9
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer in the documentation
|
11
|
+
# and/or other materials provided with the distribution.
|
12
|
+
|
13
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
14
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
15
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
16
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
17
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
18
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
19
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
20
|
+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
21
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
22
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
23
|
+
|
24
|
+
# The views and conclusions contained in the software and documentation are those
|
25
|
+
# of the authors and should not be interpreted as representing official policies,
|
26
|
+
# either expressed or implied, of the FreeBSD Project.
|
27
|
+
|
28
|
+
|
29
|
+
import json
|
30
|
+
import logging
|
31
|
+
import urllib.parse
|
32
|
+
|
33
|
+
import pyramid.request
|
34
|
+
import pyramid.response
|
35
|
+
import requests
|
36
|
+
from pyramid.httpexceptions import HTTPBadGateway, HTTPFound
|
37
|
+
from pyramid.view import view_config
|
38
|
+
|
39
|
+
from c2cgeoportal_commons.lib.url import Url
|
40
|
+
from c2cgeoportal_geoportal.lib import is_intranet
|
41
|
+
from c2cgeoportal_geoportal.lib.caching import get_region
|
42
|
+
from c2cgeoportal_geoportal.lib.common_headers import Cache
|
43
|
+
from c2cgeoportal_geoportal.lib.functionality import get_functionality
|
44
|
+
from c2cgeoportal_geoportal.views.proxy import Proxy
|
45
|
+
|
46
|
+
_LOG = logging.getLogger(__name__)
|
47
|
+
_CACHE_REGION = get_region("std")
|
48
|
+
|
49
|
+
|
50
|
+
class PrintProxy(Proxy):
|
51
|
+
"""All the view concerned the print."""
|
52
|
+
|
53
|
+
def __init__(self, request: pyramid.request.Request):
|
54
|
+
Proxy.__init__(self, request)
|
55
|
+
|
56
|
+
@view_config(route_name="printproxy_capabilities") # type: ignore[misc]
|
57
|
+
def capabilities(self) -> pyramid.response.Response:
|
58
|
+
"""Get print capabilities."""
|
59
|
+
templates = get_functionality("print_template", self.request, is_intranet(self.request))
|
60
|
+
|
61
|
+
# get query string
|
62
|
+
params = dict(self.request.params)
|
63
|
+
query_string = urllib.parse.urlencode(params)
|
64
|
+
|
65
|
+
resp, content = self._capabilities(
|
66
|
+
templates, query_string, self.request.method, self.request.referrer, self.request.host
|
67
|
+
)
|
68
|
+
|
69
|
+
response = self._build_response(resp, content, Cache.PRIVATE, "print")
|
70
|
+
# Mapfish print will check the referrer header to return the capabilities.
|
71
|
+
response.vary += ("Referrer", "Referer")
|
72
|
+
return response
|
73
|
+
|
74
|
+
@_CACHE_REGION.cache_on_arguments()
|
75
|
+
def _capabilities(
|
76
|
+
self, templates: list[str], query_string: dict[str, str], method: str, referrer: str, host: str
|
77
|
+
) -> tuple[requests.Response, str]:
|
78
|
+
del query_string # Just for caching
|
79
|
+
del method # Just for caching
|
80
|
+
del referrer # Just for caching
|
81
|
+
del host # Just for caching
|
82
|
+
|
83
|
+
# Get URL
|
84
|
+
_url = self.request.get_organization_print_url() + "/capabilities.json"
|
85
|
+
|
86
|
+
response = self._proxy(Url(_url))
|
87
|
+
response.raise_for_status()
|
88
|
+
|
89
|
+
if self.request.method == "GET":
|
90
|
+
try:
|
91
|
+
capabilities = response.json()
|
92
|
+
except json.decoder.JSONDecodeError:
|
93
|
+
_LOG.exception("Unable to parse capabilities: %s", response.text)
|
94
|
+
raise HTTPBadGateway(response.text) # pylint: disable=raise-missing-from
|
95
|
+
|
96
|
+
capabilities["layouts"] = list(
|
97
|
+
layout for layout in capabilities["layouts"] if layout["name"] in templates
|
98
|
+
)
|
99
|
+
|
100
|
+
pretty = self.request.params.get("pretty", "false") == "true"
|
101
|
+
content = json.dumps(
|
102
|
+
capabilities, separators=None if pretty else (",", ":"), indent=4 if pretty else None
|
103
|
+
)
|
104
|
+
else:
|
105
|
+
content = ""
|
106
|
+
|
107
|
+
return response, content
|
108
|
+
|
109
|
+
@view_config(route_name="printproxy_report_create") # type: ignore[misc]
|
110
|
+
def report_create(self) -> pyramid.response.Response:
|
111
|
+
"""Create PDF."""
|
112
|
+
return self._proxy_response(
|
113
|
+
"print",
|
114
|
+
Url(
|
115
|
+
f"{ self.request.get_organization_print_url()}/report.{self.request.matchdict.get('format')}"
|
116
|
+
),
|
117
|
+
)
|
118
|
+
|
119
|
+
@view_config(route_name="printproxy_status") # type: ignore[misc]
|
120
|
+
def status(self) -> pyramid.response.Response:
|
121
|
+
"""PDF status."""
|
122
|
+
return self._proxy_response(
|
123
|
+
"print",
|
124
|
+
Url(
|
125
|
+
f"{self.request.get_organization_print_url()}/status/{self.request.matchdict.get('ref')}.json"
|
126
|
+
),
|
127
|
+
)
|
128
|
+
|
129
|
+
@view_config(route_name="printproxy_cancel") # type: ignore[misc]
|
130
|
+
def cancel(self) -> pyramid.response.Response:
|
131
|
+
"""PDF cancel."""
|
132
|
+
return self._proxy_response(
|
133
|
+
"print",
|
134
|
+
Url(f"{self.request.get_organization_print_url()}/cancel/{self.request.matchdict.get('ref')}"),
|
135
|
+
)
|
136
|
+
|
137
|
+
@view_config(route_name="printproxy_report_get") # type: ignore[misc]
|
138
|
+
def report_get(self) -> pyramid.response.Response:
|
139
|
+
"""Get the PDF."""
|
140
|
+
url = Url(f"{self.request.get_organization_print_url()}/report/{self.request.matchdict.get('ref')}")
|
141
|
+
if self.request.registry.settings.get("print_get_redirect", False):
|
142
|
+
raise HTTPFound(location=url.url())
|
143
|
+
return self._proxy_response("print", url)
|
@@ -0,0 +1,192 @@
|
|
1
|
+
# Copyright (c) 2012-2024, Camptocamp SA
|
2
|
+
# All rights reserved.
|
3
|
+
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
# list of conditions and the following disclaimer.
|
9
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer in the documentation
|
11
|
+
# and/or other materials provided with the distribution.
|
12
|
+
|
13
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
14
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
15
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
16
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
17
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
18
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
19
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
20
|
+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
21
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
22
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
23
|
+
|
24
|
+
# The views and conclusions contained in the software and documentation are those
|
25
|
+
# of the authors and should not be interpreted as representing official policies,
|
26
|
+
# either expressed or implied, of the FreeBSD Project.
|
27
|
+
|
28
|
+
import json
|
29
|
+
import logging
|
30
|
+
import math
|
31
|
+
import urllib.parse
|
32
|
+
from decimal import Decimal
|
33
|
+
from json.decoder import JSONDecodeError
|
34
|
+
from typing import Any
|
35
|
+
|
36
|
+
import geojson
|
37
|
+
import pyramid.request
|
38
|
+
import requests
|
39
|
+
from pyramid.httpexceptions import HTTPBadRequest, HTTPInternalServerError, HTTPNotFound
|
40
|
+
from pyramid.i18n import TranslationStringFactory
|
41
|
+
from pyramid.view import view_config
|
42
|
+
|
43
|
+
from c2cgeoportal_geoportal.lib.common_headers import Cache, set_common_headers
|
44
|
+
from c2cgeoportal_geoportal.views.raster import Raster
|
45
|
+
|
46
|
+
_LOG = logging.getLogger(__name__)
|
47
|
+
|
48
|
+
_ = TranslationStringFactory("c2cgeoportal")
|
49
|
+
|
50
|
+
|
51
|
+
class Profile(Raster):
|
52
|
+
"""All the view concerned the profile."""
|
53
|
+
|
54
|
+
def __init__(self, request: pyramid.request.Request):
|
55
|
+
Raster.__init__(self, request)
|
56
|
+
|
57
|
+
@staticmethod
|
58
|
+
def _to_filtered(points: list[dict[str, Any]], layers: list[str]) -> list[dict[str, Any]]:
|
59
|
+
profile = []
|
60
|
+
for point in points:
|
61
|
+
filtered_alts = {key: value for key, value in point["alts"].items() if key in layers}
|
62
|
+
profile.append(
|
63
|
+
{
|
64
|
+
"dist": point["dist"],
|
65
|
+
"values": filtered_alts,
|
66
|
+
"x": point["easting"],
|
67
|
+
"y": point["northing"],
|
68
|
+
}
|
69
|
+
)
|
70
|
+
return profile
|
71
|
+
|
72
|
+
def _get_profile_service_data(
|
73
|
+
self, layers: list[str], geom: dict[str, Any], rasters: dict[str, Any], nb_points: int
|
74
|
+
) -> list[dict[str, Any]]:
|
75
|
+
request = f"{rasters[layers[0]]['url']}/profile.json?{urllib.parse.urlencode({'geom': geom, 'nbPoints': nb_points, 'distinct_points': 'true'})}"
|
76
|
+
response = requests.get(request, timeout=10)
|
77
|
+
if not response.ok:
|
78
|
+
_LOG.error("profile request %s failed with status code %s", request, response.status_code)
|
79
|
+
raise HTTPInternalServerError(
|
80
|
+
f"Failed to fetch profile data from internal request: \
|
81
|
+
{response.status_code} {response.reason}"
|
82
|
+
)
|
83
|
+
|
84
|
+
try:
|
85
|
+
points = json.loads(response.content)
|
86
|
+
except (TypeError, JSONDecodeError) as exc:
|
87
|
+
_LOG.exception("profile request %s failed", request)
|
88
|
+
raise HTTPInternalServerError("Failed to decode JSON response from internal request") from exc
|
89
|
+
|
90
|
+
return self._to_filtered(points, layers)
|
91
|
+
|
92
|
+
@view_config(route_name="profile.json", renderer="fast_json") # type: ignore[misc]
|
93
|
+
def json(self) -> dict[str, Any]:
|
94
|
+
"""Answer to /profile.json."""
|
95
|
+
_, points = self._compute_points()
|
96
|
+
set_common_headers(self.request, "profile", Cache.PUBLIC_NO)
|
97
|
+
return {"profile": points}
|
98
|
+
|
99
|
+
def _compute_points(self) -> tuple[list[str], list[dict[str, Any]]]:
|
100
|
+
"""Compute the alt=fct(dist) array."""
|
101
|
+
geom = geojson.loads(self.request.params["geom"], object_hook=geojson.GeoJSON.to_instance)
|
102
|
+
nb_points = int(self.request.params["nbPoints"])
|
103
|
+
coords = []
|
104
|
+
service_results: list[dict[str, Any]] = []
|
105
|
+
|
106
|
+
layers: list[str]
|
107
|
+
if "layers" in self.request.params:
|
108
|
+
rasters = {}
|
109
|
+
layers = self.request.params["layers"].split(",")
|
110
|
+
for layer in layers:
|
111
|
+
if layer in self.rasters:
|
112
|
+
rasters[layer] = self.rasters[layer]
|
113
|
+
else:
|
114
|
+
raise HTTPNotFound(f"Layer {layer!s} not found")
|
115
|
+
else:
|
116
|
+
rasters = self.rasters
|
117
|
+
layers = list(rasters.keys())
|
118
|
+
layers.sort()
|
119
|
+
|
120
|
+
service_layers = [layer for layer in layers if rasters[layer].get("type") == "external_url"]
|
121
|
+
|
122
|
+
if len(service_layers) > 0:
|
123
|
+
urls = [rasters[layer]["url"] for layer in service_layers]
|
124
|
+
if len(set(urls)) != 1:
|
125
|
+
raise HTTPBadRequest("All service layers must have the same URL.")
|
126
|
+
service_results = self._get_profile_service_data(service_layers, geom, rasters, nb_points)
|
127
|
+
if len(service_layers) < len(layers):
|
128
|
+
coords = [(point["x"], point["y"]) for point in service_results]
|
129
|
+
else:
|
130
|
+
return layers, service_results
|
131
|
+
|
132
|
+
if len(service_results) == 0:
|
133
|
+
points: list[dict[str, Any]] = []
|
134
|
+
|
135
|
+
dist = 0
|
136
|
+
prev_coord = None
|
137
|
+
coords = self._create_points(geom.coordinates, nb_points)
|
138
|
+
for coord in coords:
|
139
|
+
if prev_coord is not None:
|
140
|
+
dist += self._dist(prev_coord, coord)
|
141
|
+
_LOG.info("new dist %s", dist)
|
142
|
+
|
143
|
+
values = {}
|
144
|
+
for ref in list(rasters.keys()):
|
145
|
+
value = self._get_raster_value(self.rasters[ref], ref, coord[0], coord[1])
|
146
|
+
values[ref] = value
|
147
|
+
_LOG.info("values %s", values)
|
148
|
+
|
149
|
+
# 10cm accuracy is enough for distances
|
150
|
+
rounded_dist = Decimal(str(dist)).quantize(Decimal("0.1"))
|
151
|
+
points.append({"dist": rounded_dist, "values": values, "x": coord[0], "y": coord[1]})
|
152
|
+
prev_coord = coord
|
153
|
+
return layers, points
|
154
|
+
else:
|
155
|
+
additional_layers = [layer for layer in layers if layer not in service_layers]
|
156
|
+
for point in service_results:
|
157
|
+
for ref in additional_layers:
|
158
|
+
value = self._get_raster_value(self.rasters[ref], ref, point["x"], point["y"])
|
159
|
+
point["values"][ref] = value
|
160
|
+
return layers, service_results
|
161
|
+
|
162
|
+
@staticmethod
|
163
|
+
def _dist(coord1: tuple[float, float], coord2: tuple[float, float]) -> float:
|
164
|
+
"""Compute the distance between 2 points."""
|
165
|
+
return math.sqrt(math.pow(coord1[0] - coord2[0], 2.0) + math.pow(coord1[1] - coord2[1], 2.0))
|
166
|
+
|
167
|
+
def _create_points(self, coords: list[tuple[float, float]], nb_points: int) -> list[tuple[float, float]]:
|
168
|
+
"""Add some points in order to reach roughly the asked number of points."""
|
169
|
+
total_length = 0
|
170
|
+
prev_coord = None
|
171
|
+
for coord in coords:
|
172
|
+
if prev_coord is not None:
|
173
|
+
total_length += self._dist(prev_coord, coord)
|
174
|
+
prev_coord = coord
|
175
|
+
|
176
|
+
if total_length == 0.0:
|
177
|
+
return coords
|
178
|
+
|
179
|
+
result: list[tuple[float, float]] = []
|
180
|
+
prev_coord = None
|
181
|
+
for coord in coords:
|
182
|
+
if prev_coord is not None:
|
183
|
+
cur_length = self._dist(prev_coord, coord)
|
184
|
+
cur_nb_points = max(int(nb_points * cur_length / total_length + 0.5), 1)
|
185
|
+
dx = (coord[0] - prev_coord[0]) / float(cur_nb_points)
|
186
|
+
dy = (coord[1] - prev_coord[1]) / float(cur_nb_points)
|
187
|
+
for i in range(1, cur_nb_points + 1):
|
188
|
+
result.append((prev_coord[0] + dx * i, prev_coord[1] + dy * i))
|
189
|
+
else:
|
190
|
+
result.append((coord[0], coord[1]))
|
191
|
+
prev_coord = coord
|
192
|
+
return result
|
@@ -0,0 +1,261 @@
|
|
1
|
+
# Copyright (c) 2011-2025, Camptocamp SA
|
2
|
+
# All rights reserved.
|
3
|
+
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
# list of conditions and the following disclaimer.
|
9
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer in the documentation
|
11
|
+
# and/or other materials provided with the distribution.
|
12
|
+
|
13
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
14
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
15
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
16
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
17
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
18
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
19
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
20
|
+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
21
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
22
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
23
|
+
|
24
|
+
# The views and conclusions contained in the software and documentation are those
|
25
|
+
# of the authors and should not be interpreted as representing official policies,
|
26
|
+
# either expressed or implied, of the FreeBSD Project.
|
27
|
+
|
28
|
+
|
29
|
+
import logging
|
30
|
+
import sys
|
31
|
+
from typing import Any
|
32
|
+
|
33
|
+
import pyramid.request
|
34
|
+
import pyramid.response
|
35
|
+
import requests
|
36
|
+
from pyramid.httpexceptions import HTTPBadGateway, exception_response
|
37
|
+
|
38
|
+
from c2cgeoportal_commons.lib.url import Url
|
39
|
+
from c2cgeoportal_geoportal.lib.caching import get_region
|
40
|
+
from c2cgeoportal_geoportal.lib.common_headers import Cache, set_common_headers
|
41
|
+
from c2cgeoportal_geoportal.views import restrict_headers
|
42
|
+
|
43
|
+
_LOG = logging.getLogger(__name__)
|
44
|
+
_CACHE_REGION = get_region("std")
|
45
|
+
|
46
|
+
|
47
|
+
class Proxy:
|
48
|
+
"""Some methods used by all the proxy."""
|
49
|
+
|
50
|
+
def __init__(self, request: pyramid.request.Request):
|
51
|
+
self.request = request
|
52
|
+
self.host_forward_host = request.registry.settings.get("host_forward_host", [])
|
53
|
+
self.headers_whitelist = request.registry.settings.get("headers_whitelist", [])
|
54
|
+
self.headers_blacklist = request.registry.settings.get("headers_blacklist", [])
|
55
|
+
self.http_options = self.request.registry.settings.get("http_options", {})
|
56
|
+
|
57
|
+
def _proxy(
|
58
|
+
self,
|
59
|
+
url: Url,
|
60
|
+
params: dict[str, str] | None = None,
|
61
|
+
method: str | None = None,
|
62
|
+
cache: bool = False,
|
63
|
+
body: bytes | None = None,
|
64
|
+
headers: dict[str, str] | None = None,
|
65
|
+
) -> requests.models.Response:
|
66
|
+
# Get query string
|
67
|
+
params = dict(self.request.params) if params is None else params
|
68
|
+
url = url.clone().add_query(params, True)
|
69
|
+
|
70
|
+
_LOG.debug("Send query to URL:\n%s.", url)
|
71
|
+
|
72
|
+
if method is None:
|
73
|
+
method = self.request.method
|
74
|
+
|
75
|
+
headers = dict(self.request.headers if headers is None else headers)
|
76
|
+
|
77
|
+
# Forward request to target (without Host Header).
|
78
|
+
# The original Host will be added back by pyramid.
|
79
|
+
if url.hostname not in self.host_forward_host and "Host" in headers:
|
80
|
+
headers.pop("Host")
|
81
|
+
|
82
|
+
# Forward the request tracking ID to the other service. This will allow to follow the logs belonging
|
83
|
+
# to a single request coming from the user
|
84
|
+
headers.setdefault("X-Request-ID", self.request.c2c_request_id)
|
85
|
+
# If we really want to respect the specification, we should chain with the content of the previous
|
86
|
+
# proxy, see also:
|
87
|
+
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded
|
88
|
+
forwarded = {"for": self.request.client_addr, "proto": self.request.scheme}
|
89
|
+
if "Host" in self.request.headers:
|
90
|
+
forwarded["host"] = self.request.headers["Host"]
|
91
|
+
forwarded_str = ";".join(["=".join(e) for e in forwarded.items()])
|
92
|
+
if "Forwarded" in headers:
|
93
|
+
headers["Forwarded"] = ",".join([headers["Forwarded"], forwarded_str])
|
94
|
+
else:
|
95
|
+
headers["Forwarded"] = forwarded_str
|
96
|
+
# Set alternative "X-Forwarded" headers
|
97
|
+
for forwarded_elements in reversed(headers["Forwarded"].split(",")):
|
98
|
+
for element in forwarded_elements.split(";"):
|
99
|
+
key, value = element.split("=")
|
100
|
+
header_key = f"X-Forwarded-{key.capitalize()}"
|
101
|
+
header_value = headers.get(header_key)
|
102
|
+
headers[header_key] = value if header_value is None else ", ".join([header_value, value])
|
103
|
+
|
104
|
+
if not cache:
|
105
|
+
headers["Cache-Control"] = "no-cache"
|
106
|
+
|
107
|
+
if method in ("POST", "PUT") and body is None:
|
108
|
+
body = self.request.body
|
109
|
+
|
110
|
+
headers = restrict_headers(headers, self.headers_whitelist, self.headers_blacklist)
|
111
|
+
|
112
|
+
try:
|
113
|
+
if method in ("POST", "PUT"):
|
114
|
+
response = requests.request(
|
115
|
+
method, url.url(), data=body, headers=headers, **self.http_options
|
116
|
+
)
|
117
|
+
else:
|
118
|
+
response = requests.request(method, url.url(), headers=headers, **self.http_options)
|
119
|
+
except Exception:
|
120
|
+
errors = ["Error '%s' while getting the URL:", "%s", "Method: %s", "--- With headers ---", "%s"]
|
121
|
+
args1 = [
|
122
|
+
sys.exc_info()[0],
|
123
|
+
url,
|
124
|
+
method,
|
125
|
+
"\n".join(
|
126
|
+
[
|
127
|
+
f"{h}: {v if h not in ('Authorization', 'Cookie') else '***'}"
|
128
|
+
for h, v in list(headers.items())
|
129
|
+
]
|
130
|
+
),
|
131
|
+
]
|
132
|
+
if method in ("POST", "PUT") and body is not None:
|
133
|
+
errors += ["--- Query with body ---", "%s"]
|
134
|
+
args1.append(body.decode("utf-8"))
|
135
|
+
_LOG.exception("\n".join(errors), *args1)
|
136
|
+
|
137
|
+
raise HTTPBadGateway( # pylint: disable=raise-missing-from
|
138
|
+
"Error on backend, See logs for detail"
|
139
|
+
)
|
140
|
+
|
141
|
+
if not response.ok:
|
142
|
+
errors = [
|
143
|
+
"Error '%s' in response of URL:",
|
144
|
+
"%s",
|
145
|
+
"Status: %d",
|
146
|
+
"Method: %s",
|
147
|
+
"--- With headers ---",
|
148
|
+
"%s",
|
149
|
+
]
|
150
|
+
args2: list[str | int] = [
|
151
|
+
response.reason,
|
152
|
+
url.url(),
|
153
|
+
response.status_code,
|
154
|
+
method,
|
155
|
+
"\n".join(
|
156
|
+
[
|
157
|
+
f"{h}: {v if h not in ('Authorization', 'Cookie') else '***'}"
|
158
|
+
for h, v in list(headers.items())
|
159
|
+
]
|
160
|
+
),
|
161
|
+
]
|
162
|
+
if method in ("POST", "PUT") and body is not None:
|
163
|
+
errors += ["--- Query with body ---", "%s"]
|
164
|
+
args2.append(body.decode("utf-8"))
|
165
|
+
errors += ["--- Return content ---", "%s"]
|
166
|
+
args2.append(response.text)
|
167
|
+
_LOG.error("\n".join(errors), *args2)
|
168
|
+
|
169
|
+
raise exception_response(response.status_code)
|
170
|
+
if not response.headers.get("Content-Type", "").startswith("image/"):
|
171
|
+
_LOG.debug("Get result for URL: %s:\n%s.", url, body)
|
172
|
+
|
173
|
+
return response
|
174
|
+
|
175
|
+
@_CACHE_REGION.cache_on_arguments()
|
176
|
+
def _proxy_cache(self, host: str, method: str, *args: Any, **kwargs: Any) -> pyramid.response.Response:
|
177
|
+
# Method is only for the cache
|
178
|
+
del host, method
|
179
|
+
|
180
|
+
kwargs["cache"] = True
|
181
|
+
return self._proxy(*args, **kwargs)
|
182
|
+
|
183
|
+
def _proxy_response(
|
184
|
+
self,
|
185
|
+
service_name: str,
|
186
|
+
url: Url,
|
187
|
+
headers_update: dict[str, str] | None = None,
|
188
|
+
public: bool = False,
|
189
|
+
**kwargs: Any,
|
190
|
+
) -> pyramid.response.Response:
|
191
|
+
if headers_update is None:
|
192
|
+
headers_update = {}
|
193
|
+
cache = kwargs.get("cache", False)
|
194
|
+
if cache is True:
|
195
|
+
response = self._proxy_cache(url, self.request.host, self.request.method, **kwargs)
|
196
|
+
else:
|
197
|
+
response = self._proxy(url, **kwargs)
|
198
|
+
|
199
|
+
cache_control = (
|
200
|
+
(Cache.PUBLIC if public else Cache.PRIVATE)
|
201
|
+
if cache
|
202
|
+
else (Cache.PUBLIC_NO if public else Cache.PRIVATE_NO)
|
203
|
+
)
|
204
|
+
return self._build_response(
|
205
|
+
response, response.content, cache_control, service_name, headers_update=headers_update
|
206
|
+
)
|
207
|
+
|
208
|
+
def _build_response(
|
209
|
+
self,
|
210
|
+
response: pyramid.response.Response,
|
211
|
+
content: bytes,
|
212
|
+
cache_control: Cache,
|
213
|
+
service_name: str,
|
214
|
+
headers: dict[str, str] | None = None,
|
215
|
+
headers_update: dict[str, str] | None = None,
|
216
|
+
content_type: str | None = None,
|
217
|
+
) -> pyramid.response.Response:
|
218
|
+
if headers_update is None:
|
219
|
+
headers_update = {}
|
220
|
+
headers = response.headers if headers is None else headers
|
221
|
+
|
222
|
+
# Hop-by-hop Headers are not supported by WSGI
|
223
|
+
# See:
|
224
|
+
# https://www.python.org/dev/peps/pep-3333/#other-http-features
|
225
|
+
# chapter 13.5.1 at https://www.faqs.org/rfcs/rfc2616.html
|
226
|
+
for header in [
|
227
|
+
"Connection",
|
228
|
+
"Keep-Alive",
|
229
|
+
"Proxy-Authenticate",
|
230
|
+
"Proxy-Authorization",
|
231
|
+
"te",
|
232
|
+
"Trailers",
|
233
|
+
"Transfer-Encoding",
|
234
|
+
"Upgrade",
|
235
|
+
]:
|
236
|
+
if header in headers:
|
237
|
+
del headers[header]
|
238
|
+
# Other problematic headers
|
239
|
+
for header in ["Content-Length", "Content-Location", "Content-Encoding"]:
|
240
|
+
if header in headers:
|
241
|
+
del headers[header]
|
242
|
+
|
243
|
+
headers.update(headers_update)
|
244
|
+
|
245
|
+
response = pyramid.response.Response(content, status=response.status_code, headers=headers)
|
246
|
+
|
247
|
+
return set_common_headers(
|
248
|
+
self.request, service_name, cache_control, response=response, content_type=content_type
|
249
|
+
)
|
250
|
+
|
251
|
+
@staticmethod
|
252
|
+
def _get_lower_params(params: dict[str, str]) -> dict[str, str]:
|
253
|
+
return {k.lower(): str(v).lower() for k, v in params.items()}
|
254
|
+
|
255
|
+
def get_headers(self) -> dict[str, str]:
|
256
|
+
headers: dict[str, str] = self.request.headers
|
257
|
+
if "Cookie" in headers:
|
258
|
+
headers.pop("Cookie")
|
259
|
+
if "Authorization" in headers:
|
260
|
+
headers.pop("Authorization")
|
261
|
+
return headers
|