c2cgeoportal-geoportal 2.3.5.80__py3-none-any.whl → 2.9rc2__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 +75 -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 +170 -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 +511 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-qgis.yaml +21 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.override.sample.yaml +59 -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 +15 -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 +43 -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 +295 -0
- c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_create_template/tests/test_testapp.py +48 -0
- c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/geoportal/CONST_config-schema.yaml +922 -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 +80 -0
- c2cgeoportal_geoportal/scripts/manage_users.py +140 -0
- c2cgeoportal_geoportal/scripts/pcreate.py +314 -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 +679 -0
- c2cgeoportal_geoportal/views/mapserverproxy.py +191 -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 +127 -0
- c2cgeoportal_geoportal/views/proxy.py +259 -0
- c2cgeoportal_geoportal/views/raster.py +193 -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.80.dist-info → c2cgeoportal_geoportal-2.9rc2.dist-info}/METADATA +21 -24
- c2cgeoportal_geoportal-2.9rc2.dist-info/RECORD +192 -0
- {c2cgeoportal_geoportal-2.3.5.80.dist-info → c2cgeoportal_geoportal-2.9rc2.dist-info}/WHEEL +1 -1
- c2cgeoportal_geoportal-2.9rc2.dist-info/entry_points.txt +28 -0
- c2cgeoportal_geoportal-2.9rc2.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.80.dist-info/DESCRIPTION.rst +0 -8
- c2cgeoportal_geoportal-2.3.5.80.dist-info/RECORD +0 -7
- c2cgeoportal_geoportal-2.3.5.80.dist-info/entry_points.txt +0 -22
- c2cgeoportal_geoportal-2.3.5.80.dist-info/metadata.json +0 -1
- c2cgeoportal_geoportal-2.3.5.80.dist-info/top_level.txt +0 -1
@@ -0,0 +1,176 @@
|
|
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
|
+
|
29
|
+
import inspect
|
30
|
+
import logging
|
31
|
+
from collections.abc import Callable, Mapping, Sequence
|
32
|
+
from typing import TYPE_CHECKING, Any
|
33
|
+
|
34
|
+
import pyramid.interfaces
|
35
|
+
import zope.interface
|
36
|
+
from dogpile.cache.api import NO_VALUE, CacheBackend, CachedValue, NoValue
|
37
|
+
from dogpile.cache.backends.memory import MemoryBackend
|
38
|
+
from dogpile.cache.backends.redis import RedisBackend, RedisSentinelBackend
|
39
|
+
from dogpile.cache.region import CacheRegion, make_region
|
40
|
+
from dogpile.cache.util import sha1_mangle_key
|
41
|
+
from sqlalchemy.orm.util import identity_key
|
42
|
+
|
43
|
+
from c2cgeoportal_commons.models import Base
|
44
|
+
|
45
|
+
if TYPE_CHECKING:
|
46
|
+
from dogpile.cache.api import SerializedReturnType
|
47
|
+
else:
|
48
|
+
SerializedReturnType = Any
|
49
|
+
|
50
|
+
_LOG = logging.getLogger(__name__)
|
51
|
+
_REGION: dict[str, CacheRegion] = {}
|
52
|
+
MEMORY_CACHE_DICT: dict[str, Any] = {}
|
53
|
+
|
54
|
+
|
55
|
+
def map_dbobject(item: type[Any]) -> Any:
|
56
|
+
"""Get an cache identity key for the cache."""
|
57
|
+
return identity_key(item) if isinstance(item, Base) else item
|
58
|
+
|
59
|
+
|
60
|
+
def keygen_function(namespace: Any, function: Callable[..., Any]) -> Callable[..., str]:
|
61
|
+
"""
|
62
|
+
Return a function that generates a string key.
|
63
|
+
|
64
|
+
Based on a given function as well as arguments to the returned function itself.
|
65
|
+
|
66
|
+
This is used by :meth:`.CacheRegion.cache_on_arguments` to generate a cache key from a decorated function.
|
67
|
+
"""
|
68
|
+
|
69
|
+
if namespace is None:
|
70
|
+
namespace = (function.__module__, function.__name__)
|
71
|
+
else:
|
72
|
+
namespace = (function.__module__, function.__name__, namespace)
|
73
|
+
|
74
|
+
args = inspect.getfullargspec(function)
|
75
|
+
ignore_first_argument = args[0] and args[0][0] in ("self", "cls")
|
76
|
+
|
77
|
+
def generate_key(*args: Any, **kw: Any) -> str:
|
78
|
+
if kw:
|
79
|
+
raise ValueError("key creation function does not accept keyword arguments.")
|
80
|
+
parts: list[str] = []
|
81
|
+
parts.extend(namespace)
|
82
|
+
if ignore_first_argument:
|
83
|
+
args = args[1:]
|
84
|
+
new_args = [
|
85
|
+
arg for arg in args if pyramid.interfaces.IRequest not in zope.interface.implementedBy(type(arg))
|
86
|
+
]
|
87
|
+
parts.extend(map(str, map(map_dbobject, new_args)))
|
88
|
+
return "|".join(parts)
|
89
|
+
|
90
|
+
return generate_key
|
91
|
+
|
92
|
+
|
93
|
+
def init_region(conf: dict[str, Any], region: str) -> CacheRegion:
|
94
|
+
"""Initialize the caching module."""
|
95
|
+
cache_region = get_region(region)
|
96
|
+
_configure_region(conf, cache_region)
|
97
|
+
return cache_region
|
98
|
+
|
99
|
+
|
100
|
+
def _configure_region(conf: dict[str, Any], cache_region: CacheRegion) -> None:
|
101
|
+
kwargs: dict[str, Any] = {"replace_existing_backend": True}
|
102
|
+
backend = conf["backend"]
|
103
|
+
kwargs.update({k: conf[k] for k in conf if k != "backend"})
|
104
|
+
kwargs.setdefault("arguments", {}).setdefault("cache_dict", MEMORY_CACHE_DICT)
|
105
|
+
cache_region.configure(backend, **kwargs)
|
106
|
+
|
107
|
+
|
108
|
+
def get_region(region: str) -> CacheRegion:
|
109
|
+
"""Return a cache region."""
|
110
|
+
if region not in _REGION:
|
111
|
+
_REGION[region] = make_region(function_key_generator=keygen_function)
|
112
|
+
return _REGION[region]
|
113
|
+
|
114
|
+
|
115
|
+
def invalidate_region(region: str | None = None) -> None:
|
116
|
+
"""Invalidate a cache region."""
|
117
|
+
if region is None:
|
118
|
+
for cache_region in _REGION.values():
|
119
|
+
cache_region.invalidate() # type: ignore[no-untyped-call]
|
120
|
+
else:
|
121
|
+
get_region(region).invalidate() # type: ignore[no-untyped-call]
|
122
|
+
|
123
|
+
|
124
|
+
class HybridRedisBackend(CacheBackend):
|
125
|
+
"""A Dogpile cache backend with a memory cache backend in front of a Redis backend for performance."""
|
126
|
+
|
127
|
+
def __init__(self, arguments: dict[str, Any]):
|
128
|
+
self._use_memory_cache = not arguments.pop("disable_memory_cache", False)
|
129
|
+
self._memory: CacheBackend = MemoryBackend( # type: ignore[no-untyped-call]
|
130
|
+
{"cache_dict": arguments.pop("cache_dict", {})},
|
131
|
+
)
|
132
|
+
self._redis: CacheBackend = RedisBackend(arguments) # type: ignore[no-untyped-call]
|
133
|
+
|
134
|
+
def get(self, key: str) -> CachedValue | bytes | NoValue:
|
135
|
+
value = self._memory.get(key)
|
136
|
+
if value == NO_VALUE:
|
137
|
+
val = self._redis.get_serialized(sha1_mangle_key(key.encode())) # type: ignore[no-untyped-call]
|
138
|
+
if val in (None, NO_VALUE):
|
139
|
+
return NO_VALUE
|
140
|
+
assert isinstance(val, bytes)
|
141
|
+
value = self._redis.deserializer(val) # type: ignore[misc]
|
142
|
+
if value != NO_VALUE and self._use_memory_cache:
|
143
|
+
assert isinstance(value, (CachedValue, bytes))
|
144
|
+
self._memory.set(key, value)
|
145
|
+
return value
|
146
|
+
|
147
|
+
def get_multi(self, keys: Sequence[str]) -> list[CachedValue | bytes | NoValue]:
|
148
|
+
return [self.get(key) for key in keys]
|
149
|
+
|
150
|
+
def set(self, key: str, value: CachedValue | bytes) -> None:
|
151
|
+
if self._use_memory_cache:
|
152
|
+
self._memory.set(key, value)
|
153
|
+
self._redis.set_serialized(
|
154
|
+
sha1_mangle_key(key.encode()), # type: ignore[no-untyped-call]
|
155
|
+
self._redis.serializer(value), # type: ignore[misc]
|
156
|
+
)
|
157
|
+
|
158
|
+
def set_multi(self, mapping: Mapping[str, CachedValue | bytes]) -> None:
|
159
|
+
for key, value in mapping.items():
|
160
|
+
self.set(key, value)
|
161
|
+
|
162
|
+
def delete(self, key: str) -> None:
|
163
|
+
self._memory.delete(key)
|
164
|
+
self._redis.delete(key)
|
165
|
+
|
166
|
+
def delete_multi(self, keys: Sequence[str]) -> None:
|
167
|
+
self._memory.delete_multi(keys)
|
168
|
+
self._redis.delete_multi(keys)
|
169
|
+
|
170
|
+
|
171
|
+
class HybridRedisSentinelBackend(HybridRedisBackend):
|
172
|
+
"""Same as HybridRedisBackend but using the Redis Sentinel."""
|
173
|
+
|
174
|
+
def __init__(self, arguments: dict[str, Any]):
|
175
|
+
super().__init__(arguments)
|
176
|
+
self._redis = RedisSentinelBackend(arguments) # type: ignore[no-untyped-call]
|
@@ -0,0 +1,80 @@
|
|
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
|
+
import logging
|
28
|
+
from typing import Any, cast
|
29
|
+
|
30
|
+
import c2cwsgiutils.health_check
|
31
|
+
import pyramid.config
|
32
|
+
import pyramid.request
|
33
|
+
import requests
|
34
|
+
|
35
|
+
from c2cgeoportal_geoportal.lib.checker import build_url
|
36
|
+
|
37
|
+
_LOG = logging.getLogger(__name__)
|
38
|
+
|
39
|
+
|
40
|
+
def init(config: pyramid.config.Configurator, health_check: c2cwsgiutils.health_check.HealthCheck) -> None:
|
41
|
+
"""
|
42
|
+
Initialize the check collector.
|
43
|
+
|
44
|
+
Add him in the c2cwsgichecks.
|
45
|
+
"""
|
46
|
+
global_settings = config.get_settings()
|
47
|
+
if "check_collector" not in global_settings:
|
48
|
+
return
|
49
|
+
settings = global_settings["check_collector"]
|
50
|
+
c2c_base = global_settings.get("c2c.base_path", "")
|
51
|
+
|
52
|
+
max_level = settings["max_level"]
|
53
|
+
|
54
|
+
for host in settings["hosts"]:
|
55
|
+
|
56
|
+
class Check:
|
57
|
+
def __init__(self, host: dict[str, Any]):
|
58
|
+
self.host = host
|
59
|
+
|
60
|
+
def __call__(self, request: pyramid.request.Request) -> dict[str, Any] | None:
|
61
|
+
params = request.params
|
62
|
+
display = self.host["display"]
|
63
|
+
if "host" not in params or display == params["host"]:
|
64
|
+
url_headers = build_url(
|
65
|
+
"check_collector",
|
66
|
+
f"{self.host['url'].rstrip('/')}/{c2c_base.strip('/')}/health_check",
|
67
|
+
request,
|
68
|
+
)
|
69
|
+
r = requests.get(
|
70
|
+
params={"max_level": str(self.host.get("max_level", max_level))},
|
71
|
+
timeout=120,
|
72
|
+
**url_headers, # type: ignore
|
73
|
+
)
|
74
|
+
r.raise_for_status()
|
75
|
+
return cast(dict[str, Any], r.json())
|
76
|
+
return None
|
77
|
+
|
78
|
+
health_check.add_custom_check(
|
79
|
+
name="check_collector_" + host["display"], check_cb=Check(host), level=settings["level"]
|
80
|
+
)
|
@@ -0,0 +1,295 @@
|
|
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
|
+
import json
|
29
|
+
import logging
|
30
|
+
import os
|
31
|
+
import subprocess
|
32
|
+
from collections.abc import Mapping
|
33
|
+
from time import sleep
|
34
|
+
from typing import Any, cast
|
35
|
+
from urllib.parse import urljoin
|
36
|
+
|
37
|
+
import c2cwsgiutils.health_check
|
38
|
+
import pyramid.config
|
39
|
+
import pyramid.request
|
40
|
+
import requests
|
41
|
+
|
42
|
+
_LOG = logging.getLogger(__name__)
|
43
|
+
|
44
|
+
|
45
|
+
def build_url(
|
46
|
+
name: str, path: str, request: pyramid.request.Request, headers: dict[str, str] | None = None
|
47
|
+
) -> dict[str, str | dict[str, str]]:
|
48
|
+
"""Build an URL and headers for the checkers."""
|
49
|
+
base_internal_url = request.registry.settings["checker"]["base_internal_url"]
|
50
|
+
url = urljoin(base_internal_url, path)
|
51
|
+
|
52
|
+
forward_host = request.registry.settings["checker"].get("forward_host", False)
|
53
|
+
headers = _build_headers(request, headers)
|
54
|
+
if forward_host:
|
55
|
+
headers["Host"] = request.host
|
56
|
+
|
57
|
+
_LOG.debug("%s, URL: %s", name, url)
|
58
|
+
return {"url": url, "headers": headers}
|
59
|
+
|
60
|
+
|
61
|
+
def _build_headers(request: pyramid.request.Request, headers: dict[str, str] | None = None) -> dict[str, str]:
|
62
|
+
if headers is None:
|
63
|
+
headers = {}
|
64
|
+
headers["Cache-Control"] = "no-cache"
|
65
|
+
settings = request.registry.settings.get("checker", {})
|
66
|
+
for header in settings.get("forward_headers", []):
|
67
|
+
value = request.headers.get(header)
|
68
|
+
if value is not None:
|
69
|
+
headers[header] = value
|
70
|
+
return headers
|
71
|
+
|
72
|
+
|
73
|
+
def _routes(settings: dict[str, Any], health_check: c2cwsgiutils.health_check.HealthCheck) -> None:
|
74
|
+
routes_settings = settings["routes"]
|
75
|
+
for route in routes_settings["routes"]:
|
76
|
+
if route.get("checker_name", route["name"]) not in routes_settings["disable"]:
|
77
|
+
name = "checker_routes_" + route.get("checker_name", route["name"])
|
78
|
+
|
79
|
+
class GetRequest:
|
80
|
+
"""Get the request information about the current route name."""
|
81
|
+
|
82
|
+
def __init__(self, route_name: str, type_: str) -> None:
|
83
|
+
self.route_name = route_name
|
84
|
+
self.type = type_
|
85
|
+
|
86
|
+
def __call__(self, request: pyramid.request.Request) -> str | dict[str, str]:
|
87
|
+
return build_url("route", request.route_path(self.route_name), request)[self.type]
|
88
|
+
|
89
|
+
health_check.add_url_check(
|
90
|
+
url=GetRequest(route["name"], "url"), # type: ignore
|
91
|
+
name=name,
|
92
|
+
params=route.get("params", None),
|
93
|
+
headers=GetRequest(route["name"], "headers"), # type: ignore
|
94
|
+
level=route["level"],
|
95
|
+
timeout=30,
|
96
|
+
)
|
97
|
+
|
98
|
+
|
99
|
+
def _pdf3(settings: dict[str, Any], health_check: c2cwsgiutils.health_check.HealthCheck) -> None:
|
100
|
+
print_settings = settings["print"]
|
101
|
+
if "spec" not in print_settings:
|
102
|
+
return
|
103
|
+
|
104
|
+
def check(request: pyramid.request.Request) -> None:
|
105
|
+
path = request.route_path("printproxy_report_create", format="pdf")
|
106
|
+
url_headers = build_url("Check the printproxy request (create)", path, request)
|
107
|
+
|
108
|
+
session = requests.session()
|
109
|
+
resp = session.post(json=print_settings["spec"], timeout=30, **url_headers) # type: ignore
|
110
|
+
resp.raise_for_status()
|
111
|
+
|
112
|
+
job = resp.json()
|
113
|
+
|
114
|
+
path = request.route_path("printproxy_status", ref=job["ref"])
|
115
|
+
url_headers = build_url("Check the printproxy pdf status", path, request)
|
116
|
+
done = False
|
117
|
+
while not done:
|
118
|
+
sleep(1)
|
119
|
+
resp = session.get(timeout=30, **url_headers) # type: ignore
|
120
|
+
resp.raise_for_status()
|
121
|
+
|
122
|
+
status = resp.json()
|
123
|
+
if "error" in status:
|
124
|
+
raise Exception( # pylint: disable=broad-exception-raised
|
125
|
+
f"Failed to do the printing: {status['error']}",
|
126
|
+
)
|
127
|
+
done = status["done"]
|
128
|
+
|
129
|
+
path = request.route_path("printproxy_report_get", ref=job["ref"])
|
130
|
+
url_headers = build_url("Check the printproxy pdf retrieve", path, request)
|
131
|
+
resp = session.get(timeout=30, **url_headers) # type: ignore
|
132
|
+
resp.raise_for_status()
|
133
|
+
|
134
|
+
health_check.add_custom_check(name="checker_print", check_cb=check, level=print_settings["level"])
|
135
|
+
|
136
|
+
|
137
|
+
def _fts(settings: dict[str, Any], health_check: c2cwsgiutils.health_check.HealthCheck) -> None:
|
138
|
+
fts_settings = settings["fulltextsearch"]
|
139
|
+
if fts_settings.get("disable", False):
|
140
|
+
return
|
141
|
+
|
142
|
+
def get_both(request: pyramid.request.Request) -> dict[str, str | dict[str, str]]:
|
143
|
+
return build_url("Check the fulltextsearch", request.route_path("fulltextsearch"), request)
|
144
|
+
|
145
|
+
def check(_request: pyramid.request.Request, response: pyramid.response.Response) -> None:
|
146
|
+
assert response.json()["features"], "No result"
|
147
|
+
|
148
|
+
health_check.add_url_check(
|
149
|
+
name="checker_fulltextsearch",
|
150
|
+
url=lambda r: get_both(r)["url"], # type: ignore
|
151
|
+
headers=lambda r: get_both(r)["headers"], # type: ignore
|
152
|
+
params={"query": fts_settings["search"], "limit": "1"},
|
153
|
+
check_cb=check,
|
154
|
+
level=fts_settings["level"],
|
155
|
+
)
|
156
|
+
|
157
|
+
|
158
|
+
def _themes_errors(settings: dict[str, Any], health_check: c2cwsgiutils.health_check.HealthCheck) -> None:
|
159
|
+
from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel
|
160
|
+
from c2cgeoportal_commons.models.main import Interface # pylint: disable=import-outside-toplevel
|
161
|
+
|
162
|
+
assert DBSession is not None
|
163
|
+
|
164
|
+
themes_settings = settings["themes"]
|
165
|
+
default_params = themes_settings.get("params", {})
|
166
|
+
interfaces_settings = themes_settings["interfaces"]
|
167
|
+
|
168
|
+
def check(request: pyramid.request.Request) -> None:
|
169
|
+
path = request.route_path("themes")
|
170
|
+
session = requests.session()
|
171
|
+
for (interface,) in DBSession.query(Interface.name).all():
|
172
|
+
params: dict[str, str] = {}
|
173
|
+
params.update(default_params)
|
174
|
+
params.update(interfaces_settings.get(interface, {}).get("params", {}))
|
175
|
+
params["interface"] = interface
|
176
|
+
|
177
|
+
interface_url_headers = build_url("checker_themes " + interface, path, request)
|
178
|
+
|
179
|
+
response = session.get(params=params, timeout=120, **interface_url_headers) # type: ignore
|
180
|
+
response.raise_for_status()
|
181
|
+
|
182
|
+
result = response.json()
|
183
|
+
if result["errors"]:
|
184
|
+
raise c2cwsgiutils.health_check.JsonCheckException(
|
185
|
+
f"Interface '{interface}' has error in Theme.", result["errors"]
|
186
|
+
)
|
187
|
+
|
188
|
+
health_check.add_custom_check(name="checker_themes", check_cb=check, level=themes_settings["level"])
|
189
|
+
|
190
|
+
|
191
|
+
def _lang_files(
|
192
|
+
global_settings: dict[str, Any],
|
193
|
+
settings: dict[str, Any],
|
194
|
+
health_check: c2cwsgiutils.health_check.HealthCheck,
|
195
|
+
) -> None:
|
196
|
+
lang_settings = settings["lang"]
|
197
|
+
available_locale_names = global_settings["available_locale_names"]
|
198
|
+
|
199
|
+
default_name = global_settings["default_locale_name"]
|
200
|
+
assert default_name in available_locale_names, (
|
201
|
+
f"default_locale_name '{default_name}' not in available_locale_names: "
|
202
|
+
f"{', '.join(available_locale_names)}"
|
203
|
+
)
|
204
|
+
|
205
|
+
for type_ in lang_settings.get("files", []):
|
206
|
+
for lang in available_locale_names:
|
207
|
+
if type_ == "ngeo":
|
208
|
+
url = f"/etc/geomapfish/static/{lang}.json"
|
209
|
+
else:
|
210
|
+
raise Exception( # pylint: disable=broad-exception-raised
|
211
|
+
f"Your language type value '{type_}' is not valid, available values [ngeo]",
|
212
|
+
)
|
213
|
+
|
214
|
+
name = f"checker_lang_{type_}_{lang}"
|
215
|
+
|
216
|
+
class GetRequest:
|
217
|
+
"""Get the request information about the current route name."""
|
218
|
+
|
219
|
+
def __init__(self, name: str, url: str, lang: str, type_: str) -> None:
|
220
|
+
self.name = name
|
221
|
+
self.url = url
|
222
|
+
self.lang = lang
|
223
|
+
self.type = type_
|
224
|
+
|
225
|
+
def __call__(self, request: pyramid.request.Request) -> str | Mapping[str, str]:
|
226
|
+
return build_url(
|
227
|
+
self.name,
|
228
|
+
request.static_path(
|
229
|
+
self.url.format(package=global_settings["package"], lang=self.lang)
|
230
|
+
),
|
231
|
+
request,
|
232
|
+
)[self.type]
|
233
|
+
|
234
|
+
health_check.add_url_check(
|
235
|
+
name=name,
|
236
|
+
url=GetRequest(name, url, lang, "url"), # type: ignore
|
237
|
+
headers=GetRequest(name, url, lang, "headers"), # type: ignore
|
238
|
+
level=lang_settings["level"],
|
239
|
+
)
|
240
|
+
|
241
|
+
|
242
|
+
def _phantomjs(settings: dict[str, Any], health_check: c2cwsgiutils.health_check.HealthCheck) -> None:
|
243
|
+
phantomjs_settings = settings["phantomjs"]
|
244
|
+
for route in phantomjs_settings["routes"]:
|
245
|
+
if route.get("checker_name", route["name"]) in phantomjs_settings["disable"]:
|
246
|
+
continue
|
247
|
+
|
248
|
+
class _Check:
|
249
|
+
def __init__(self, route: dict[str, Any]) -> None:
|
250
|
+
self.route = route
|
251
|
+
|
252
|
+
def __call__(self, request: pyramid.request.Request) -> None:
|
253
|
+
path = request.route_path(self.route["name"], _query=self.route.get("params", {}))
|
254
|
+
url: str = cast(str, build_url("Check", path, request)["url"])
|
255
|
+
|
256
|
+
cmd: list[str] = ["check-example", url]
|
257
|
+
env = dict(os.environ)
|
258
|
+
for name, value in self.route.get("environment", {}).items():
|
259
|
+
if isinstance(value, (list, dict)):
|
260
|
+
value = json.dumps(value)
|
261
|
+
elif not isinstance(value, str):
|
262
|
+
value = str(value)
|
263
|
+
env[name] = value
|
264
|
+
|
265
|
+
try:
|
266
|
+
subprocess.check_output(cmd, env=env, timeout=70)
|
267
|
+
except subprocess.CalledProcessError as exception:
|
268
|
+
raise Exception( # pylint: disable=broad-exception-raised
|
269
|
+
f"{' '.join(exception.cmd)} exit with code: {exception.returncode}\n"
|
270
|
+
f"{exception.output.decode('utf-8')[:10000]}"
|
271
|
+
) from exception
|
272
|
+
except subprocess.TimeoutExpired as exception:
|
273
|
+
raise Exception( # pylint: disable=broad-exception-raised
|
274
|
+
f"""Timeout:
|
275
|
+
command: {' '.join(exception.cmd)}
|
276
|
+
output:
|
277
|
+
{exception.output.decode('utf-8')}"""
|
278
|
+
) from exception
|
279
|
+
|
280
|
+
name = "checker_phantomjs_" + route.get("checker_name", route["name"])
|
281
|
+
health_check.add_custom_check(name=name, check_cb=_Check(route), level=route["level"])
|
282
|
+
|
283
|
+
|
284
|
+
def init(config: pyramid.config.Configurator, health_check: c2cwsgiutils.health_check.HealthCheck) -> None:
|
285
|
+
"""Initialize the checkers."""
|
286
|
+
global_settings = config.get_settings()
|
287
|
+
if "checker" not in global_settings:
|
288
|
+
return
|
289
|
+
settings = global_settings["checker"]
|
290
|
+
_routes(settings, health_check)
|
291
|
+
_pdf3(settings, health_check)
|
292
|
+
_fts(settings, health_check)
|
293
|
+
_themes_errors(settings, health_check)
|
294
|
+
_lang_files(global_settings, settings, health_check)
|
295
|
+
_phantomjs(settings, health_check)
|